本文52pojie:https://www.52pojie.cn/thread-1666622-1-1.html
本文juejin:https://juejin.cn/post/7124354639419408414/
作者:hans774882968以及hans774882968
获取另一个APK
打开JEB:
@Override // android.content.ContextWrapper
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
File _ = this.getDir("sctf_odex", 0);
File l = this.getDir("sctf_lib", 0);
this.odexPath = _.getAbsolutePath();
this.libPath = l.getAbsolutePath();
this.apkFileName = _.getAbsolutePath() + "/sctf.apk";
File File = new File(this.apkFileName);
Log.i("demo", "apk size:" + File.length());
if(!File.exists()) {
File.createNewFile();
this._(this.__("data"));
}
Object currentActivityThread = s.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread", new Class[0], new Object[0]);
String packageName = this.getPackageName();
WeakReference v5 = Build.VERSION.SDK_INT >= 19 ? ((WeakReference)((ArrayMap)s.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mPackages")).get(packageName)) : ((WeakReference)((HashMap)s.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mPackages")).get(packageName));
DexClassLoader dLoader = new DexClassLoader(this.apkFileName, this.odexPath, this.libPath, ((ClassLoader)s.getFieldOjbect("android.app.LoadedApk", v5.get(), "mClassLoader")));
s.setFieldOjbect("android.app.LoadedApk", "mClassLoader", v5.get(), dLoader);
Log.i("demo", "classloader:" + dLoader);
}
catch(Exception e) {
Log.i("demo", "error:" + Log.getStackTraceString(e));
e.printStackTrace();
}
}
这里有一个特殊的知识点:attachBaseContext
方法调用时机早于onCreate
。另外,查看代码易知this._(this.__("data"));
会获得一个新的apk。那我们写个获取新apk的脚本:
def get_byte(v):
return v.to_bytes(1, byteorder='little')
with open('data', 'rb') as f:
a = f.read()
with open('sctf.apk', 'wb') as g:
for i in range(len(a)):
g.write(get_byte(a[i] ^ ord("syclover"[i % 8])))
安卓父子组件通信机制
逐一查看sctf.demo.myapplication
下的每个类。我们发现t
这个类是主界面。如下:
package sctf.demo.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.math.BigInteger;
import java.security.MessageDigest;
public class t extends AppCompatActivity {
@Override // android.support.v4.app.FragmentActivity
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
String v6;
TextView tv = (TextView)this.findViewById(0x7F07008B); // id:textView2
Button bu = (Button)this.findViewById(0x7F070023); // id:button2
if(requestCode == 1 && resultCode == -1) {
String key = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update("syclover".getBytes());
v6 = new BigInteger(1, md.digest()).toString(16);
}
catch(Exception e) {
e.printStackTrace();
goto label_29;
}
key = v6;
label_29:
if(f.encode(data.getStringExtra("data_return"), key).equals("~8t808_8A8n848r808i8d8-8w808r8l8d8}8")) {
tv.setVisibility(0);
bu.setVisibility(4);
return;
}
Toast.makeText(this.getApplicationContext(), "one more step", 1).show();
}
}
@Override // android.support.v7.app.AppCompatActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F09001C); // layout:activity
((Button)this.findViewById(0x7F070023)).setOnClickListener(new View.OnClickListener() { // id:button2
@Override // android.view.View$OnClickListener
public void onClick(View v) {
Intent intent = new Intent("sctf.demo.myapplication.MAIN");
intent.addCategory("sctf.demo.myapplication.LAUNCHER");
t.this.startActivityForResult(intent, 1);
}
});
}
}
这里使用了Intent
类,找到参考链接1,可知这是安卓开发里父子组件的通信机制。因为sctf.demo.myapplication
下就那几个类,我们很容易猜到子组件是s
类:
package sctf.demo.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class s extends AppCompatActivity {
@Override // android.support.v7.app.AppCompatActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F09001D); // layout:activity_main
Button bu = (Button)this.findViewById(0x7F070022); // id:button
this.findViewById(0x7F07008A); // id:textView
bu.setOnClickListener(new View.OnClickListener() {
@Override // android.view.View$OnClickListener
public void onClick(View v) {
String s1 = "";
String s2 = "";
int i = 0;
String v0 = ((EditText)this.findViewById(0x7F070037)).getText().toString(); // id:editText
if(v0.length() == 30) {
while(i < 12) {
s1 = s1 + v0.charAt(i);
++i;
}
String v1_1 = f.sctf(s1);
while(i < 30) {
s2 = s2 + v0.charAt(i);
++i;
}
if(v1_1.equals("c2N0ZntXM2xjMG1l")) {
Intent intent = new Intent();
intent.putExtra("data_return", s2);
s.this.setResult(-1, intent);
s.this.finish();
return;
}
Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
return;
}
Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
}
});
}
}
由参考链接1可知,"data_return"
属性用来传递输入串的最后18个字符。
f.sctf()
是base64编码,所以把"c2N0ZntXM2xjMG1l"
进行base64解码,就是flag的前12个字符。
f.encode()
如下:
public static String encode(String str, String key) {
int s = str.length();
int c = key.length();
StringBuilder t = new StringBuilder();
int f;
for(f = 0; f < s; ++f) {
t.append(str.charAt(f));
t.append(key.charAt(f / c));
}
return t.toString();
}
所以取出"~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
下标为偶数的(0-indexed)字符就是flag的后18个字符。
import base64
lef = base64.b64decode("c2N0ZntXM2xjMG1l").decode()
print(lef)
enc, ky = "", ""
goal = "~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
for i in range(len(goal)):
if i % 2:
ky += goal[i]
else:
enc += goal[i]
print(enc, ky)
print("ans = %s" % (lef + enc))
参考链接
- 安卓父子组件通信机制:
onActivityResult() + Intent()
:https://blog.csdn.net/weixin_41008021/article/details/90346700