本题难度适中,非常适合入门复现。
目标实现无敌版鼠鼠,效果图:

所有org、dump、fix以及分析文件都已放在附件(除了游戏题目由附件大小限制没放)。
视频版教程 :96aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1K9h3I4A6j5X3W2D9K9g2)9J5k6h3y4G2L8g2)9J5c8Y4k6A6k6r3g2G2i4K6u0r3b7W2j5I4f1s2N6r3M7s2A6F1c8h3&6y4
Unity 逆向,Mono 打包的 apk,找到 Assembly-CSharp.dll,确定为 Mono 类的逆向,关键的业务代码也就在这个 dll 里,然而一看发现不对劲,拉一个正常的对比。

既然是加密了,动态肯定会解密,于是就是了解一下 Unity 其 Mono 打包成 apk 的加载流程。
于是去查看 libmono.so 哪里动了手脚,尝试通过 frida hook mono_image_open_from_data_with_name,因为该函数是加载 Assembly-Csharp.dll 的函数。
但是有 frida 检测,似乎无法直接到达加载该 dll 的时机,所以现在去解决 frida 检测。
先看了 init_array 看看反调试是不是写这了,然后发现 0x1F120 的函数,显然是动态解密一些数据的函数,不管是调试还是模拟执行都行,不过通过 LLM 辅助解析,写个 idapython 脚本,该函数就是传一个常量,返回解密后的数据到 bss 数据段。
同时写一段注释到每个字符串所在位置,完整脚本如下。
执行完所有字符串所在位置就确定了

再回到frida检测时候弹出的字符串是 hack detected, type:frida,然后就程序就退出了,那么通过该字符串定位到此处,不难就发现了

既然确定了退出的地方,那么其实可以跳过如何检测的逻辑,直接结束本题了。
但现在毕竟是在复现题目,是为了学到更多东西,我开始去理清整个加载流程,因为还有很多检测加载细节。
所以现在就是分岔口,由于已经知道检测后退出的地方,一种选择可以直接去 patch 重签名,直接跟着其他 wp 即可,另一种选择就是去认真审计一下加载的流程,检测的流程,更加仔细感受一下整个题目。
再者我在这题中不想调试,原因也是很多时候其实不好调试,一般都是通过模拟执行或者 Hook 框架来确定一些值,当然这题其实能调,过往我也喜欢调试来确定,但是这题我想锻炼通过审计、模拟执行以及 Hook 框架来处理问题。
我认为看一篇文章最核心的还是思考作者如何想到这一点,往往文章中缺失的一点是作者在写这篇文章的思考过程与试错点,这些过程写起来很麻烦而且因人而异,但我写 write up 会更多的写是怎么思考以及试错后得到信息与解决方法,如果只是这里 patch 一下,那里跑个脚本,其实学不到什么东西,我的整个分析也许做不到足够细,但我想我在这篇文章中尽量把大局整理明白,还有些细节与思考需要读者自行去尝试。
先开始理清整个加载流程
Zygote 进程:
应用进程启动 (Java 早期):
System.loadLibrary("sec2021") 触发 (Java 层):
进入 Native 层 (Linker 阶段):
JNI 握手 (Native 层):
回到 Java 层 (attachBaseContext):
现在给出每个细节依据,先从 AndroidManifest.xml 找到了
android:name="com.tencent.games.sec2021.Sec2021Application",确定了这是apk启动的入口点,在该类中。

先是 System.loadLibrary("sec2021"),那么运行了 init_array,去 ida 审计发现有两个函数,第一个 init 函数其实就是初始化 method(第二个 init 函数不重要)。

随后去看 JNIOnload,通过 java 层会调用 native 层的 initialize 函数,而且 init_array 会初始化了 methods,不难想象 JNIOnload 里是动态注册该函数的过程。

JNIOnload 执行完后,返回运行到 attachBaseContext 方法,其中执行 initialize naitive 函数。
至此,整个运行流程就明白了,不难猜测检测函数都在 initialize native 函数进行初始化的(毕竟 init_array 与 JNIOnload 无事发生),这里 frida 等 libsec2021.so 加载了 hook 上即可。
开始审计 initialize 函数,该函数地址在 init_array 初始化 methods 的时候就可以找到是在 5464 地址,这里 coreObj 初始化了一个对象,后面经常使用。

经过初始化后可以发现各种检测函数的注册是在 initialize_process,在函数刚开始的地方,将一些函数注册到了刚刚初始化的对象上,且这些注册函数还上了虚假控制流和控制流平坦化。

显然是里面有好康的但出题人不让康,所以上了不好康的混淆,这里直接猜测里面就是检测 frida ida 这些,因为在上面的小节我们已经把字符串混淆都处理差不多了,没有发现 frida、ida 这些字符串,说明大概率就在这里面了。
接下来也都是一些函数注册到了刚刚的结构体上,手动翻了这些函数,不过逆向的角度来说不太重要,我们关注点是哪里检测的问题,直到翻到 initialize_process 末尾,调用到了之前标记的函数。

这里我的标记链是 alreadyChecked_c -> alreadyChecked_b -> alreadyChecked_a,而 alreadyChecked_a 就是检测到 hack detected, type:%s 退出的地方。
刚刚的理清流程、审计函数主要是分析了

于是可以发现这几个调用点,基本上都可以回溯到 intialize 函数进行了注册,作为逆向可以不进行关注注册细节,我们只需要分析其中一个调用逻辑即可,就拿刚刚 initialize_process 的末尾来举例,我们只需要把 BEQ 直接 patch 成 B,恒跳转到不会失败的分支即可。

同样处理其他几个调用点,写一个 frida 脚本(frida版本 16.1.4,Android 10,Pixel 3XL)
成功 patch,也不会被检测到退出了!

既然检测已过,且该程序的加解密都是落地的,所以基本上接下来没有什么困难的了。
先去获得正确的 Assembly-CSharp.dll,同样开始文章刚开始的思路,去 hook libmono.so 的 mono_image_open_from_data_with_name 函数。

去看了眼该函数,发现该函数不能正常反编译(这属于系统函数所以不正常),但也不是花指令之类,所以估计是sec里对mono还进行了修改(从之前字符串解密的的text与libmono也有感觉)。
但具体如何修改mono其实也不用在意,只需要找一个合适的时机 dump 出 mono 即可看到 libsec 是修改后的 mono 了,也就是只要落地,那么不管咋加密都能 dump 出来,毕竟 frida 检测都过了。
dump 出来(可以拿SoFixer修一下,但只是看看函数也可以不修),如果对比正常的 libmono.so 会发现多个跳转,但之后的代码就一样了。

所以直接 hook sub_190C74,再根据函数原型。
正常的 hook dump Assembly-CSharp.dll 出来即可。
但先等等看游戏逻辑,上面分析有些细节没讲
真相是该流程下 libmono.so 在正常调用到 mono_image_open_from_data_with_name 时候,做了一个跳转到 sec2021(可以 frida 看参数或者调试确定),处理过 dll 再跳回正常执行。
其实也是收回伏笔了,一开始我们找到的 Assembly-CSharp 静态是给加密的,所以正常读取肯定会做过处理后再使用,具体就是跳到了 0001CEE0 地址位置。
该函数审计可得
至此,整理一下目前所得的信息
将 dll 拖进 dnSpy,游戏逻辑很简单,直接 nop 掉检测碰到激光的分支即可。

现在要想一下如何处理 libsec 了,首先是之前在 frida 修改的检测跳转,手动去把 BEQ 改成 B 就可以。
检测干掉了还差一处地方,也就是 mono_image_open_from_data_with_name 入口的跳转,是特别处理了开头不是 MZ 的 dll,由于我们将 patch 好的 Assembly-CSharp,且已经实现了解密,所以不需要走 libsec 中解密的流程。
这里直接将过了 MZ 检测的 dll 直接去加载 dll,不去做解密操作了,同样是把这里的 BEQ 改成 B 即可。

然而在重打包的地方有处小坑,猜测是由于 apktool b 重打包的时候会尝试编译所有的 XML、resoureces.arsc 重新生成资源索引表,导致 libsec 读取某些资源的偏移量出问题了。
其实具体的检测函数我没在文中细写(分析的 i64 看附件),实际上很多检测函数要读 assets 或是其他内容进行 crc 校验等,估计是在这个过程如果用 apktool 会导致无法对应内容从而用 apktool 重打包会导致运行失败。(当然这是猜测,如果有师傅有其他理解欢迎讨论)
于是我们就直接用压缩软件直接打开 apk,不去解压,将目标 Assembly-CSharp 和 libsec2021 换成我们 patch 好的,再重新签名。

feaK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2P5r3S2&6k6h3q4^5i4K6u0W2j5$3!0E0i4K6u0r3x3U0l9J5x3g2)9J5c8U0l9@1i4K6u0r3x3o6c8Q4x3V1k6Y4M7$3I4S2j5U0t1H3x3U0q4Q4x3X3c8H3M7X3g2Q4x3X3c8S2L8X3c8J5L8$3W2V1i4K6u0r3
418K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6X3L8%4u0#2L8g2)9J5k6i4m8Z5M7q4)9K6c8X3g2^5N6s2u0S2i4K6y4p5M7r3q4Y4k6g2)9J5y4e0y4V1x3g2)9J5y4e0t1$3k6X3W2D9N6r3g2J5i4K6t1#2x3$3c8V1K9h3N6W2M7%4c8Q4x3U0f1J5y4X3!0J5k6r3g2J5j5Y4W2Q4x3U0f1K6k6s2k6A6k6i4N6K6i4K6t1#2x3U0k6@1P5i4m8W2K9h3c8Q4x3U0f1K6k6o6x3@1x3W2)9J5y4X3q4E0M7q4)9K6b7X3#2G2k6q4)9K6c8s2k6A6k6i4N6@1K9s2u0W2j5h3c8Q4x3U0k6S2L8i4m8Q4x3@1u0@1K9h3c8Q4x3@1b7I4y4o6t1H3y4K6M7#2
613K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6D9K9h3&6C8L8r3g2&6M7r3W2F1k6#2)9J5k6i4c8G2M7q4)9J5c8X3N6K6L8r3q4T1x3U0l9J5x3g2)9J5k6s2m8J5k6g2)9J5c8W2)9J5x3$3q4H3M7q4)9J5y4f1f1#2i4K6t1#2z5o6S2Q4x3U0f1^5y4W2)9J5y4f1f1$3i4K6t1#2z5f1g2Q4x3U0f1&6x3l9`.`.
[ 安卓系统 (Android OS) ]
↓
[ Unity 引擎层 (libunity.so) ]
↓
[ Mono 虚拟机层 (libmono.so) ] <--- 你可以 Hook 这里
↓
[ 托管代码层 (Assembly-CSharp.dll) ] <--- 你可以直接反编译这里
↓
[ JIT 编译器 ] -> [ 生成内存中的 ARM 指令 ] -> [ CPU 执行 ]
[ 安卓系统 (Android OS) ]
↓
[ Unity 引擎层 (libunity.so) ]
↓
[ Mono 虚拟机层 (libmono.so) ] <--- 你可以 Hook 这里
↓
[ 托管代码层 (Assembly-CSharp.dll) ] <--- 你可以直接反编译这里
↓
[ JIT 编译器 ] -> [ 生成内存中的 ARM 指令 ] -> [ CPU 执行 ]
var libbase = Module.findBaseAddress("libmono.so");
console.log("libbase", libbase);
var addr = Module.findExportByName("libmono.so", "mono_image_open_from_data_with_name");
console.log("mono_image_open_from_data_with_name", addr);
var libbase = Module.findBaseAddress("libmono.so");
console.log("libbase", libbase);
var addr = Module.findExportByName("libmono.so", "mono_image_open_from_data_with_name");
console.log("mono_image_open_from_data_with_name", addr);
============================================================
doSome 字符串解密器 v1.1
============================================================
[*] 批量解密模式...
============================================================
doSome 字符串解密结果
============================================================
[ 28] @ 0x372c4: "Author: saitexie walterjxli"
[ 59] @ 0x372e3: "Do you know how unity mono works?"
[ 96] @ 0x37308: "res/drawable-xhdpi-v4/ -> assets/bin/Data/Managed/"
[ 150] @ 0x3733e: "initialize"
[ 164] @ 0x3734c: "()I"
[ 171] @ 0x37353: "com/tencent/games/sec2021/Sec2021Application"
[ 219] @ 0x37383: "com/tencent/games/sec2021/Sec2021IPC"
[ 259] @ 0x373ab: "hack detected, risk score:%d"
[ 291] @ 0x373cb: "getApplicationInfo"
[ 313] @ 0x373e1: "()Landroid/content/pm/ApplicationInfo;"
[ 355] @ 0x3740b: "getFilesDir"
[ 370] @ 0x3741a: "()Ljava/io/File;"
[ 390] @ 0x3742e: "sourceDir"
[ 403] @ 0x3743b: "packageName"
[ 418] @ 0x3744a: "nativeLibraryDir"
[ 438] @ 0x3745e: "getAbsolutePath"
[ 457] @ 0x37471: "/proc/self/status"
[ 478] @ 0x37486: "TracerPid:"
[ 492] @ 0x37494: "diediedie"
[ 505] @ 0x374a1: "/proc/self/maps"
[ 524] @ 0x374b4: "rb"
[ 530] @ 0x374ba: "delete"
[ 540] @ 0x374c4: "%zx-%zx %c%c%c%c %x %x:%x %u %s"
[ 575] @ 0x374e7: "android/os/Debug"
[ 595] @ 0x374fb: "isDebuggerConnected"
[ 618] @ 0x37512: "sec2021"
[ 629] @ 0x3751d: "getClass"
[ 641] @ 0x37529: "getName"
[ 652] @ 0x37534: "getSuperclass"
[ 669] @ 0x37545: "android/app/Application"
[ 696] @ 0x37560: "java/lang/Class"
[ 715] @ 0x37573: "()Ljava/lang/Class;"
[ 738] @ 0x3758a: "()Landroid/content/pm/ApplicationInfo;"
[ 780] @ 0x375b4: "()Ljava/io/File;"
[ 800] @ 0x375c8: "()Ljava/lang/String;"
[ 824] @ 0x375e0: "Assembly-CSharp.dll"
[ 847] @ 0x375f7: "Mono.Security.dll"
[ 868] @ 0x3760c: "mscorlib.dll"
[ 884] @ 0x3761c: "System.Core.dll"
[ 903] @ 0x3762f: "System.dll"
[ 917] @ 0x3763d: "UnityEngine.dll"
[ 936] @ 0x37650: "UnityEngine.Networking.dll"
[ 966] @ 0x3766e: "UnityEngine.PlaymodeTestsRunner.dll"
[1005] @ 0x37695: "UnityEngine.UI.dll"
[1027] @ 0x376ab: "base.apk"
[1039] @ 0x376b7: "android/content/Context"
[1066] @ 0x376d2: "()Ljava/lang/ClassLoader;"
[1095] @ 0x376ef: "()Ljava/lang/String;"
[1119] @ 0x37707: "zip file"
[1131] @ 0x37713: "libsec2021.so"
[1148] @ 0x37724: "%s/%s"
[1157] @ 0x3772d: "dalvik.system.PathClassLoader"
[1190] @ 0x3774e: "toString"
[1202] @ 0x3775a: "getClassLoader"
[1220] @ 0x3776c: "Ljava/lang/String;"
[1242] @ 0x37782: "%s%s"
[1250] @ 0x3778a: "/app/data/libbugly/crash.info"
[1283] @ 0x377ab: "can you crack me?"
[1304] @ 0x377c0: "__optional__"
[1320] @ 0x377d0: "cc/binmt/signature/PmsHookApplication"
[1361] @ 0x377f9: "com/cloudinject/feature/App"
[1392] @ 0x37818: "np/manager/FuckSign"
[1415] @ 0x3782f: "java/lang/ClassLoader"
[1440] @ 0x37848: "findClass"
[1453] @ 0x37855: "(Ljava/lang/String;)Ljava/lang/Class;"
[1494] @ 0x3787e: "getPackageManager"
[1515] @ 0x37893: "()Landroid/content/pm/PackageManager;"
[1556] @ 0x378bc: "getPackageName"
[1574] @ 0x378ce: "getPackageInfo"
[1592] @ 0x378e0: "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"
[1649] @ 0x37919: "signatures"
[1663] @ 0x37927: "[Landroid/content/pm/Signature;"
[1698] @ 0x3794a: "toByteArray"
[1713] @ 0x37959: "()[B"
[1721] @ 0x37961: "java/io/ByteArrayInputStream"
[1753] @ 0x37981: "<init>"
[1763] @ 0x3798b: "([B)V"
[1772] @ 0x37994: "java/security/cert/CertificateFactory"
[1813] @ 0x379bd: "getInstance"
[1828] @ 0x379cc: "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;"
[1891] @ 0x37a0b: "X.509"
[1900] @ 0x37a14: "generateCertificate"
[1923] @ 0x37a2b: "(Ljava/io/InputStream;)Ljava/security/cert/Certificate;"
[1982] @ 0x37a66: "getEncoded"
[1996] @ 0x37a74: "java/security/MessageDigest"
============================================================
共解密 85 个字符串
============================================================
============================================================
doSome 字符串解密器 v1.1
============================================================
[*] 批量解密模式...
============================================================
doSome 字符串解密结果
============================================================
[ 28] @ 0x372c4: "Author: saitexie walterjxli"
[ 59] @ 0x372e3: "Do you know how unity mono works?"
[ 96] @ 0x37308: "res/drawable-xhdpi-v4/ -> assets/bin/Data/Managed/"
[ 150] @ 0x3733e: "initialize"
[ 164] @ 0x3734c: "()I"
[ 171] @ 0x37353: "com/tencent/games/sec2021/Sec2021Application"
[ 219] @ 0x37383: "com/tencent/games/sec2021/Sec2021IPC"
[ 259] @ 0x373ab: "hack detected, risk score:%d"
[ 291] @ 0x373cb: "getApplicationInfo"
[ 313] @ 0x373e1: "()Landroid/content/pm/ApplicationInfo;"
[ 355] @ 0x3740b: "getFilesDir"
[ 370] @ 0x3741a: "()Ljava/io/File;"
[ 390] @ 0x3742e: "sourceDir"
[ 403] @ 0x3743b: "packageName"
[ 418] @ 0x3744a: "nativeLibraryDir"
[ 438] @ 0x3745e: "getAbsolutePath"
[ 457] @ 0x37471: "/proc/self/status"
[ 478] @ 0x37486: "TracerPid:"
[ 492] @ 0x37494: "diediedie"
[ 505] @ 0x374a1: "/proc/self/maps"
[ 524] @ 0x374b4: "rb"
[ 530] @ 0x374ba: "delete"
[ 540] @ 0x374c4: "%zx-%zx %c%c%c%c %x %x:%x %u %s"
[ 575] @ 0x374e7: "android/os/Debug"
[ 595] @ 0x374fb: "isDebuggerConnected"
[ 618] @ 0x37512: "sec2021"
[ 629] @ 0x3751d: "getClass"
[ 641] @ 0x37529: "getName"
[ 652] @ 0x37534: "getSuperclass"
[ 669] @ 0x37545: "android/app/Application"
[ 696] @ 0x37560: "java/lang/Class"
[ 715] @ 0x37573: "()Ljava/lang/Class;"
[ 738] @ 0x3758a: "()Landroid/content/pm/ApplicationInfo;"
[ 780] @ 0x375b4: "()Ljava/io/File;"
[ 800] @ 0x375c8: "()Ljava/lang/String;"
[ 824] @ 0x375e0: "Assembly-CSharp.dll"
[ 847] @ 0x375f7: "Mono.Security.dll"
[ 868] @ 0x3760c: "mscorlib.dll"
[ 884] @ 0x3761c: "System.Core.dll"
[ 903] @ 0x3762f: "System.dll"
[ 917] @ 0x3763d: "UnityEngine.dll"
[ 936] @ 0x37650: "UnityEngine.Networking.dll"
[ 966] @ 0x3766e: "UnityEngine.PlaymodeTestsRunner.dll"
[1005] @ 0x37695: "UnityEngine.UI.dll"
[1027] @ 0x376ab: "base.apk"
[1039] @ 0x376b7: "android/content/Context"
[1066] @ 0x376d2: "()Ljava/lang/ClassLoader;"
[1095] @ 0x376ef: "()Ljava/lang/String;"
[1119] @ 0x37707: "zip file"
[1131] @ 0x37713: "libsec2021.so"
[1148] @ 0x37724: "%s/%s"
[1157] @ 0x3772d: "dalvik.system.PathClassLoader"
[1190] @ 0x3774e: "toString"
[1202] @ 0x3775a: "getClassLoader"
[1220] @ 0x3776c: "Ljava/lang/String;"
[1242] @ 0x37782: "%s%s"
[1250] @ 0x3778a: "/app/data/libbugly/crash.info"
[1283] @ 0x377ab: "can you crack me?"
[1304] @ 0x377c0: "__optional__"
[1320] @ 0x377d0: "cc/binmt/signature/PmsHookApplication"
[1361] @ 0x377f9: "com/cloudinject/feature/App"
[1392] @ 0x37818: "np/manager/FuckSign"
[1415] @ 0x3782f: "java/lang/ClassLoader"
[1440] @ 0x37848: "findClass"
[1453] @ 0x37855: "(Ljava/lang/String;)Ljava/lang/Class;"
[1494] @ 0x3787e: "getPackageManager"
[1515] @ 0x37893: "()Landroid/content/pm/PackageManager;"
[1556] @ 0x378bc: "getPackageName"
[1574] @ 0x378ce: "getPackageInfo"
[1592] @ 0x378e0: "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"
[1649] @ 0x37919: "signatures"
[1663] @ 0x37927: "[Landroid/content/pm/Signature;"
[1698] @ 0x3794a: "toByteArray"
[1713] @ 0x37959: "()[B"
[1721] @ 0x37961: "java/io/ByteArrayInputStream"
[1753] @ 0x37981: "<init>"
[1763] @ 0x3798b: "([B)V"
[1772] @ 0x37994: "java/security/cert/CertificateFactory"
[1813] @ 0x379bd: "getInstance"
[1828] @ 0x379cc: "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;"
[1891] @ 0x37a0b: "X.509"
[1900] @ 0x37a14: "generateCertificate"
[1923] @ 0x37a2b: "(Ljava/io/InputStream;)Ljava/security/cert/Certificate;"
[1982] @ 0x37a66: "getEncoded"
[1996] @ 0x37a74: "java/security/MessageDigest"
============================================================
共解密 85 个字符串
============================================================
import idaapi
import idautils
import idc
DATA_ADDR = 0x372a8
BSS_DATA_ADDR = 0x39998
KEY_TABLE_REF = 0x34EB7
KEY_TABLE_LEN = 17
def get_bytes_at(addr, size):
return bytes([idc.get_wide_byte(addr + i) for i in range(size)])
def decrypt_string(idx):
key1 = idc.get_wide_byte(DATA_ADDR + idx)
key2 = idc.get_wide_byte(DATA_ADDR + idx + 1)
length = key1 ^ key2
if length == 0:
return ("", 0, idx + 4)
decrypted = bytearray()
for i in range(length):
enc_byte = idc.get_wide_byte(DATA_ADDR + idx + 2 + i)
key_idx = (i // KEY_TABLE_LEN) * KEY_TABLE_LEN
key_offset = i - key_idx
key_byte = idc.get_wide_byte(KEY_TABLE_REF + key_offset)
dec_byte = enc_byte ^ key1 ^ key_byte
decrypted.append(dec_byte)
try:
result = decrypted.decode('utf-8')
except:
try:
result = decrypted.decode('latin-1')
except:
result = decrypted.hex()
next_idx = idx + 2 + length + 2
return (result, length, next_idx)
def calc_checksum(data_bytes):
checksum = 0xFF
for b in data_bytes:
checksum ^= b
return checksum & 0xFF
def decrypt_all_strings(max_count=100, max_offset=2000):
results = []
idx = 0
count = 0
print("=" * 60)
print("doSome 字符串解密结果")
print("=" * 60)
while count < max_count and idx < max_offset:
try:
decrypted, length, next_idx = decrypt_string(idx)
if length > 0 and length < 256:
if decrypted and all(c.isprintable() or c in '\r\n\t' for c in decrypted):
results.append({
'index': idx,
'data_addr': hex(DATA_ADDR + idx),
'length': length,
'decrypted': decrypted
})
print(f"[{idx:4d}] @ {DATA_ADDR + idx:#x}: \"{decrypted}\"")
count += 1
if next_idx <= idx:
idx += 1
else:
idx = next_idx
except Exception as e:
print(f"Error at idx {idx}: {e}")
idx += 1
print("=" * 60)
print(f"共解密 {len(results)} 个字符串")
print("=" * 60)
return results
def decrypt_by_param(param_value):
decrypted, length, _ = decrypt_string(param_value)
print(f"doSome({param_value}) = \"{decrypted}\" (len={length})")
return decrypted
def find_doSome_calls():
dosome_addr = idc.get_name_ea_simple("doSome")
if dosome_addr == idc.BADADDR:
print("未找到 doSome 函数")
return []
print(f"doSome 函数地址: {dosome_addr:#x}")
print("-" * 60)
calls = []
for xref in idautils.XrefsTo(dosome_addr):
caller_addr = xref.frm
caller_func = idaapi.get_func(caller_addr)
if caller_func:
func_name = idc.get_func_name(caller_func.start_ea)
else:
func_name = "unknown"
param = None
curr_search_addr = caller_addr
for _ in range(10):
curr_search_addr = idc.prev_head(curr_search_addr)
if curr_search_addr == idc.BADADDR:
break
mnem = idc.print_insn_mnem(curr_search_addr)
if mnem in ('MOV', 'MOVS', 'MOVW', 'LDR'):
if idc.get_operand_type(curr_search_addr, 0) == idc.o_reg and idc.get_operand_value(curr_search_addr, 0) == 0:
op_type = idc.get_operand_type(curr_search_addr, 1)
if op_type in (idc.o_imm, idc.o_mem):
op_value = idc.get_operand_value(curr_search_addr, 1)
if op_value != idc.BADADDR:
param = op_value
break
if param is not None:
try:
decrypted, length, _ = decrypt_string(param)
calls.append({
'call_addr': caller_addr,
'func_name': func_name,
'param': param,
'decrypted': decrypted
})
print(f"{caller_addr:#x} in {func_name}: doSome({param}) = \"{decrypted}\"")
except:
print(f"{caller_addr:#x} in {func_name}: doSome({param}) = <解密失败>")
else:
calls.append({
'call_addr': caller_addr,
'func_name': func_name,
'param': None,
'decrypted': None
})
print(f"{caller_addr:#x} in {func_name}: doSome(?) = <参数未知>")
return calls
def patch_comments():
calls = find_doSome_calls()
for call in calls:
if call['decrypted']:
comment = f"doSome({call['param']}) = \"{call['decrypted']}\""
idc.set_cmt(call['call_addr'], comment, 0)
print(f"已添加注释 @ {call['call_addr']:#x}: {comment}")
print(f"\n共添加 {sum(1 for c in calls if c['decrypted'])} 个注释")
if __name__ == "__main__":
print("\n" + "=" * 60)
print("doSome 字符串解密器 v1.1")
print("=" * 60 + "\n")
results = decrypt_all_strings(max_count=100)
print("\n[*] 查找 doSome 调用点...")
patch_comments()
import idaapi
import idautils
import idc
DATA_ADDR = 0x372a8
BSS_DATA_ADDR = 0x39998
KEY_TABLE_REF = 0x34EB7
KEY_TABLE_LEN = 17
def get_bytes_at(addr, size):
return bytes([idc.get_wide_byte(addr + i) for i in range(size)])
def decrypt_string(idx):
key1 = idc.get_wide_byte(DATA_ADDR + idx)
key2 = idc.get_wide_byte(DATA_ADDR + idx + 1)
length = key1 ^ key2
if length == 0:
return ("", 0, idx + 4)
decrypted = bytearray()
for i in range(length):
enc_byte = idc.get_wide_byte(DATA_ADDR + idx + 2 + i)
key_idx = (i // KEY_TABLE_LEN) * KEY_TABLE_LEN
key_offset = i - key_idx
key_byte = idc.get_wide_byte(KEY_TABLE_REF + key_offset)
dec_byte = enc_byte ^ key1 ^ key_byte
decrypted.append(dec_byte)
try:
result = decrypted.decode('utf-8')
except:
try:
result = decrypted.decode('latin-1')
except:
result = decrypted.hex()
next_idx = idx + 2 + length + 2
return (result, length, next_idx)
def calc_checksum(data_bytes):
checksum = 0xFF
for b in data_bytes:
checksum ^= b
return checksum & 0xFF
def decrypt_all_strings(max_count=100, max_offset=2000):
results = []
idx = 0
count = 0
print("=" * 60)
print("doSome 字符串解密结果")
print("=" * 60)
while count < max_count and idx < max_offset:
try:
decrypted, length, next_idx = decrypt_string(idx)
if length > 0 and length < 256:
if decrypted and all(c.isprintable() or c in '\r\n\t' for c in decrypted):
results.append({
'index': idx,
'data_addr': hex(DATA_ADDR + idx),
'length': length,
'decrypted': decrypted
})
print(f"[{idx:4d}] @ {DATA_ADDR + idx:#x}: \"{decrypted}\"")
count += 1
if next_idx <= idx:
idx += 1
else:
idx = next_idx
except Exception as e:
print(f"Error at idx {idx}: {e}")
idx += 1
print("=" * 60)
print(f"共解密 {len(results)} 个字符串")
print("=" * 60)
return results
def decrypt_by_param(param_value):
decrypted, length, _ = decrypt_string(param_value)
print(f"doSome({param_value}) = \"{decrypted}\" (len={length})")
return decrypted
def find_doSome_calls():
dosome_addr = idc.get_name_ea_simple("doSome")
if dosome_addr == idc.BADADDR:
print("未找到 doSome 函数")
return []
print(f"doSome 函数地址: {dosome_addr:#x}")
print("-" * 60)
calls = []
for xref in idautils.XrefsTo(dosome_addr):
caller_addr = xref.frm
caller_func = idaapi.get_func(caller_addr)
if caller_func:
func_name = idc.get_func_name(caller_func.start_ea)
else:
func_name = "unknown"
param = None
curr_search_addr = caller_addr
for _ in range(10):
curr_search_addr = idc.prev_head(curr_search_addr)
if curr_search_addr == idc.BADADDR:
break
mnem = idc.print_insn_mnem(curr_search_addr)
if mnem in ('MOV', 'MOVS', 'MOVW', 'LDR'):
if idc.get_operand_type(curr_search_addr, 0) == idc.o_reg and idc.get_operand_value(curr_search_addr, 0) == 0:
op_type = idc.get_operand_type(curr_search_addr, 1)
if op_type in (idc.o_imm, idc.o_mem):
op_value = idc.get_operand_value(curr_search_addr, 1)
if op_value != idc.BADADDR:
param = op_value
break
if param is not None:
try:
decrypted, length, _ = decrypt_string(param)
calls.append({
'call_addr': caller_addr,
'func_name': func_name,
'param': param,
'decrypted': decrypted
})
print(f"{caller_addr:#x} in {func_name}: doSome({param}) = \"{decrypted}\"")
except:
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 3天前
被P.Z编辑
,原因: