【Android 逆向】【攻防世界】easy-dex

android,逆向,攻防,世界,easy,dex · 浏览次数 : 75

小编点评

这段代码的目的是在运行时将一个dex文件中的classes.dex加载到内存中并读取其内容,并将该内容反编译成一个Android应用的字节码。 **代码执行步骤:** 1. 通过 `Process` 获取系统中名为 `libnative.so` 的库文件。 2. 通过 `Memory.patchCode` 将二进制代码嵌入 `classes.dex` 文件中。 3. 使用 `a` 变量存储二进制代码的长度。 4. 通过循环读取 `classes.dex` 文件中的二进制数据并将其转换为字节数组。 5. 将字节数组转换为 Base64 字符串。 6. 使用 `base64a` 变量将 Base64 字符串解码为一个 byte数组。 7. 将解码后的 byte数组转换为 `android.util.bytearray`。 8. 使用 `android.util.bytearray` 的方法将字节数组转换为 `android.app.Activity` 的字节码。 **文件写位置:** 这段代码将将 `classes.dex` 和 `resources.arsc` 文件的二进制代码嵌入到 `classes.dex` 文件中。这些文件会在应用程序运行时加载到内存中。 **结论:** 这段代码通过在运行时将 dex 文件加载到内存中并反编译成字节码,实现了对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}

与【Android 逆向】【攻防世界】easy-dex相似的内容:

【Android 逆向】【攻防世界】easy-dex

这一题不easy,不知为何叫这个名字。。。。 1. apk 安装到手机,不知所云,各种亮瞎眼闪光 2. jadx 打开apk,一行java代码都没有,打开AndroidManifest看看

【Android 逆向】【攻防世界】easy-apk

apk 安装到手机,随便输入点内容,提示错误 2. apk 拖入到jadx中看看 public class MainActivity extends AppCompatActivity { /* JADX INFO: Access modifiers changed from: protected

【Android 逆向】【攻防世界】easy-so

1. apk安装到手机,随便输入点内容,提示错误 2. jadx打开apk btn.setOnClickListener(new View.OnClickListener() { // from class: com.testjava.jack.pingan2.MainActivity.1 @Ove

【Android 逆向】【攻防世界】基础android

1. 下载并安装apk,提示要输入密码 2. apk拖入到jadx中看一下 this.login.setOnClickListener(new View.OnClickListener() { // from class: com.example.test.ctf02.MainActivity.1

【Android 逆向】【攻防世界】android2.0

这是一道纯算法还原题 1. apk安装到手机,提示输入flag,看来输入就是flag 2. jadx 打开apk查看 this.button.setOnClickListener(new View.OnClickListener() { // from class: com.example.test

【Android 逆向】【攻防世界】APK逆向

1. apk安装到手机,提示输入flag 2. jadx打开apk 定位到checkSN方法 public boolean checkSN(String userName, String sn) { if (userName != null) { try { if (userName.length(

【Android 逆向】【攻防世界】人民的名义-抓捕赵德汉1-200

1. 这一题下载下来是个jar文件,感觉很android关系不大,但还是放在了mobile这个分类下了 2. 直接java jar运行,提示需要输入密码 # java -jar 169e139f152e45d5ae634223fe53e6be.jar Enter password: 1234 Inc

【Android 逆向】【攻防世界】ill-intentions

1. apk 安装到手机, 啥输入框都没有 2. apk拖入到jadx中看看 public class MainActivity extends Activity { @Override // android.app.Activity public void onCreate(Bundle save

【Android 逆向】【攻防世界】boomshakalaka-3

1. apk 安装到手机,是一个cocos2dx 写的打飞机的游戏 题目描述跟得分有关(题目描述: play the game, get the highest score) 2. jadx 打开apk public class FirstTest extends Cocos2dxActivity

【Android 逆向】【攻防世界】app1

1. apk安装到手机, 老套路了 2. jadx打开 this.btn.setOnClickListener(new View.OnClickListener() { // from class: com.example.yaphetshan.tencentgreat.MainActivity.1