【Android 逆向】【攻防世界】easy-dex
这一题不easy,不知为何叫这个名字。。。。
1. apk 安装到手机,不知所云,各种亮瞎眼闪光
2. jadx 打开apk,一行java代码都没有,打开AndroidManifest看看
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.a.sample.findmydex" platformBuildVersionCode="24" platformBuildVersionName="7">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24"/>
<application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:hasCode="false" android:allowBackup="false" android:fullBackupContent="false">
<activity android:label="@string/app_name" android:name="android.app.NativeActivity" android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.lib_name" android:value="native"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.a.sample.findmydex.MainActivity">
<intent-filter>
<action android:name="com.a.sample.findmydex.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
注意这一行<meta-data android:name="android.app.lib_name" android:value="native"/>
还有android:hasCode="false"
说明代码在native层
2. so拖入到IDA中进行分析
符号表中看到一个叫android_main
的函数,打开看看
,函数比较大,看留下的日志可以得出,需要我们摇晃手机100次的样子;先摇了再说
2023-03-22 15:21:29.741 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 17 times to go~
2023-03-22 15:21:29.891 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 15 times to go~
2023-03-22 15:21:30.041 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 13 times to go~
2023-03-22 15:21:30.192 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 11 times to go~
2023-03-22 15:21:30.207 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 9 times to go~
2023-03-22 15:21:30.342 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 7 times to go~
2023-03-22 15:21:30.491 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 5 times to go~
2023-03-22 15:21:30.658 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 3 times to go~
2023-03-22 15:21:30.790 6951-6976/? I/FindMyDex: Oh yeah~ You Got it~ 1 times to go~
看来实在监听我们摇晃的次数,达到次数后就打开界面
达到100次后,这里有一串代码
if ( v14 == 100 )
{
if ( time(0) - v6 > 9 )
{
_android_log_print(4, "FindMyDex", "OH~ You are too slow. Please try again");
qmemcpy(v3, &unk_7004, (size_t)off_43A18);
v10 = 0;
}
else
{
v20 = v6;
if ( uncompress(dest, &destLen, (const Bytef *)v3, (uLong)off_43A18) )
_android_log_print(5, "FindMyDex", "Dangerous operation detected.");
v21 = open(filename, 577, 511);
if ( !v21 )
_android_log_print(5, "FindMyDex", "Something wrong with the permission.");
write(v21, dest, destLen);
close(v21);
free(dest);
free(v3);
if ( access(name, 0) && mkdir(name, 0x1FFu) )
_android_log_print(5, "FindMyDex", "Something wrong with the permission..");
sub_2368(a1);
remove(filename);
_android_log_print(4, "FindMyDex", "Congratulations!! You made it!");
sub_2250(a1);
v10 = 0x80000000;
v6 = v20;
}
}
感觉实在写文件,然后又删了,看看删之前的sub_2368函数
if ( (*(int (__fastcall **)(_DWORD, JNIEnv **, _DWORD))(**(_DWORD **)(*(_DWORD *)(a1 + 12) + 4) + 16))(
*(_DWORD *)(*(_DWORD *)(a1 + 12) + 4),
&v21,
0) != -1 )
{
v4 = (*v21)->FindClass(v21, "android/app/Activity");
v5 = (*v21)->GetMethodID(v21, v4, "getClassLoader", "()Ljava/lang/ClassLoader;");
v6 = (*v21)->CallObjectMethod(v21, *(jobject *)(*(_DWORD *)(a1 + 12) + 12), v5);
v7 = (*v21)->NewStringUTF(v21, v25);
v8 = (*v21)->NewStringUTF(v21, v22);
v9 = (*v21)->FindClass(v21, "dalvik/system/DexClassLoader");
v10 = (*v21)->GetMethodID(
v21,
v9,
"<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");
v11 = (*v21)->NewObject(v21, v9, v10, v7, v8, 0, v6);
v12 = (*v21)->FindClass(v21, "android/content/ContextWrapper");
v13 = (*v21)->GetFieldID(v21, v12, "mBase", "Landroid/content/Context;");
v14 = (*v21)->GetObjectField(v21, *(_DWORD *)(*(_DWORD *)(a1 + 12) + 12), v13);
v15 = (*v21)->GetObjectClass(v21, v14);
v16 = (*v21)->GetFieldID(v21, v15, "mPackageInfo", "Landroid/app/LoadedApk;");
v17 = (*v21)->GetObjectField(v21, v14, v16);
v18 = (*v21)->GetObjectClass(v21, v17);
v19 = (*v21)->GetFieldID(v21, v18, "mClassLoader", "Ljava/lang/ClassLoader;");
(*v21)->GetObjectField(v21, v17, v19);
(*v21)->SetObjectField(v21, v17, v19, v11);
}
return _stack_chk_guard - v27;
}
这一看就是在动态加载dex,那么可以怀疑时so中动态释放出来了dex,然后加载
4. hook 以下remove 看一下文件写在哪了
var lib_handler = Process.findModuleByName("libnative.so")
var dst_addr = new NativePointer(lib_handler.base.add(0x00002762 + 1))
console.log("==== " + dst_addr)
Interceptor.attach(dst_addr, {
onEnter:function(args) {
console.log("==== args: " + ptr(args[0]).readCString())
console.log("==== r0: " + print_dump(this.context.r0 ))
}
})
得出:/data/data/com.a.sample.findmydex/files/classes.dex
5. nop remove 不能让他删了,好取出文件
var lib_handler = Process.findModuleByName("libnative.so")
var dst_addr = new NativePointer(lib_handler.base.add(0x00002762))
Memory.patchCode(dst_addr, 4, function (code) {
var cw = new ArmWriter(code, { pc: dst_addr });
cw.putNop()
cw.flush();
});
成功得到classes.dex
6. 将classes.dex 和 之前反编译出来的resources.arsc 一起拖入jadx中就可以看到代码了
public class MainActivity extends u {
private static byte[] m = {-120, 77, -14, -38, 17, 5, -42, 44, -32, 109, 85, 31, 24, -91, -112, -83, 64, -83, Byte.MIN_VALUE, 84, 5, -94, -98, -30, 18, 70, -26, 71, 5, -99, -62, -58, 117, 29, -44, 6, 112, -4, 81, 84, 9, 22, -51, 95, -34, 12, 47, 77};
/* JADX INFO: Access modifiers changed from: private */
public static byte[] b(String str, String str2) {
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(str.getBytes()));
ArrayList arrayList = new ArrayList();
Object a = b.a(str2.getBytes());
for (byte[] bArr = new byte[16]; bufferedInputStream.read(bArr, 0, 16) != -1; bArr = new byte[16]) {
arrayList.add(b.a(bArr, 0, a));
}
ByteBuffer allocate = ByteBuffer.allocate(arrayList.size() * 16);
for (Object obj : arrayList.toArray()) {
allocate.put((byte[]) obj);
}
return allocate.array();
} catch (Exception e) {
return new byte[1];
}
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // android.support.v7.a.u, android.support.v4.a.v, android.support.v4.a.p, android.app.Activity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
((Button) findViewById(R.id.button)).setOnClickListener(new a(this, (EditText) findViewById(R.id.edit_text), this));
}
}
通过分析(查看其他大佬的分析)可知,这是用了twofish算法实现的加密,twofish 首先时对称加密输出byte数组,然后通过base64生成字符串输出,推测m就是没有被base64编码过的byte数组,处理成base64后,
import base64
a = [-120, 77, -14, -38, 17, 5, -42, 44, -32, 109, 85, 31, 24, -91, -112, -83, 64, -83, -128, 84, 5, -94, -98, -30, 18, 70, -26, 71, 5, -99, -62, -58, 117, 29, -44, 6, 112, -4, 81, 84, 9, 22, -51, 95, -34, 12, 47, 77]
a = [i&255 for i in a]
b = base64.b64encode(bytes(a))
print(b)
日志
iE3y2hEF1izgbVUfGKWQrUCtgFQFop7iEkbmRwWdwsZ1HdQGcPxRVAkWzV/eDC9N
用在线的twofish算法解密可得出结果qwb{TH3y_Io<e_EACh_OTh3r_FOrEUER}