Turtle
用Exeinfo PE打开发现是upx壳,而且题目也提示了需要脱壳,upx壳被魔改过了,用普通脱壳工具无效,于是手动脱壳
Exeinfo PE
ps:有的魔改后的upx壳可以在010editor中改(把add0->upx0,add1->upx1……,本题为只读文件改不了)
用xdbg64打开,oep脱壳定律找到大跳转,打断点
xdbg64
F9运行,接着用ScyllaDump,oep为00000000004014E0,生成Turtle_dump.exe
Scylla Dump
Turtle_dump.exe
接着先后点击IAT Autosearch,Get Imports,Fix Dump,这里打开Turtle_dump.exe,之后打开文件夹就会看到新生成的
Turtle_dump_SCY.exe文件了
IAT Autosearch
Get Imports
将Turtle_dump_SCY.exe用IDA(64)打开,发现可以正常地看到函数了,找到main函数,F5反汇编一下
main
点开函数后可以看出sub_401550是rc4
init,而sub_40163E(标准)和sub_40175A(魔改)是两个rc4
解出flag 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 from Crypto.Cipher.ARC4 import * enc_key = bytes ([ 0xCD , 0x8F , 0x25 , 0x3D , 0xE1 , ]) enc_key += b'QJ' cipher = new(b'yekyek' ) dec_key = cipher.decrypt(enc_key)print (dec_key) enc_flag = [0 for i in range (40 )] v5 = enc_flag v5[0 ] = -8 v5[1 ] = -43 v5[2 ] = 98 v5[3 ] = -49 v5[4 ] = 67 v5[5 ] = -70 v5[6 ] = -62 v5[7 ] = 35 v5[8 ] = 21 v5[9 ] = 74 v5[10 ] = 81 v5[11 ] = 16 v5[12 ] = 39 v5[13 ] = 16 v5[14 ] = -79 v5[15 ] = -49 v5[16 ] = -60 v5[17 ] = 9 v5[18 ] = -2 v5[19 ] = -29 v5[20 ] = -97 v5[21 ] = 73 v5[22 ] = -121 v5[23 ] = -22 v5[24 ] = 89 v5[25 ] = -62 v5[26 ] = 7 v5[27 ] = 59 v5[28 ] = -87 v5[29 ] = 17 v5[30 ] = -63 v5[31 ] = -68 v5[32 ] = -3 v5[33 ] = 75 v5[34 ] = 87 v5[35 ] = -60 v5[36 ] = 126 v5[37 ] = -48 v5[38 ] = -86 v5[39 ] = 10 add_bytes = new(b'ecg4ab6' ).encrypt(b'\x00' * 40 ) flag = '' for i in range (40 ): flag += chr ((enc_flag[i] + add_bytes[i]) & 0xff ) print (flag)
flag: hgame{Y0u'r3_re4l1y_g3t_0Ut_of_th3_upX!}
尊嘟假嘟
在 MuMu模拟器 打开 apk,点击下方 尊嘟 假嘟
按钮的话就是让图片跳转,但其实你分别在两个界面点图片的能发现出现了”0.o”和”o.0”
的字符,并且是逐渐加长的
用 jadx 打开 apk,先看到有一个DexCall类
静态加载了两个本地库“zunjia”和“check”,注意到核心方法callDexMethod,动态加载.dex文件
1 public static Object callDexMethod (Context context, String dexFileName, String className, String methodName, Object input)
最后又将 dex 文件删掉,这让我想到之前做的一道 Find My
Dex 的题目,于是准备先用 frida hook 拿出这个被删掉的 dex
文件
frida-server连一下
hook 出 dex 文件的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Java .perform (function ( ) { var File = Java .use ("java.io.File" ); var DexCall = Java .use ("com.nobody.zunjia.DexCall" ); DexCall .callDexMethod .implementation = function (context, dexFileName, className, methodName, input ) { var dexDir = File .$new(context.getCacheDir (), "dex" ); console .log ("dexDir path: " + dexDir.getAbsolutePath ()); console .log ("dexFileName: " + dexFileName); console .log ("className: " + className); console .log ("methodName: " + methodName); var result = this .callDexMethod (context, dexFileName, className, methodName, input); return result; }; });
注意这里需要点击图片才能触发DexCall.callDexMethod,点callDexMethod
x 交叉引用跳转
点setText x 交叉引用跳转
而这个 JiaDu 是 ImageView
触发后
可以知道这个 dex
文件的地址是在/data/user/0/com.nobody.zunjia/cache/dex
再用一个脚本把它hook出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 Java .perform (function ( ) { var File = Java .use ("java.io.File" ); var DexCall = Java .use ("com.nobody.zunjia.DexCall" ); DexCall .copyDexFromAssets .implementation = function (context, dexFileName, dexDir ) { console .log ("[*] copyDexFromAssets called with dexFileName: " + dexFileName); var dexFile = this .copyDexFromAssets (context, dexFileName, dexDir); var dexFilePath = dexFile.getAbsolutePath (); console .log ("[*] dex file saved to: " + dexFilePath); var savePath = "/data/user/0/com.nobody.zunjia/cache/dex/dex_copy" ; var targetFile = File .$new(savePath); var inputStream = dexFile.$new(dexFilePath).getInputStream (); var outputStream = targetFile.getOutputStream (); var buffer = Java .array ('byte' , [1024 ]); var bytesRead; while ((bytesRead = inputStream.read (buffer)) !== -1 ) { outputStream.write (buffer, 0 , bytesRead); } inputStream.close (); outputStream.close (); console .log ("[*] dex file copied to: " + savePath); return dexFile; }; });
这个路径下没有权限不能直接进行提取,先把它转移到另外的文件夹,再用
adb pull 提取出来
可以看到这个路径下多了一个zunjia.dex文件,然后执行
1 mv zunjia.dex /sdcard/Download
1 adb pull /sdcard/Download/zunjia.dex D:\aaa\zunjia
这样就把 dex 文件提取到本地了,然后在 jadx
中把这个文件也添加进去,可以看到多了的内容
base64换表
有个 toast 类,里面有一个 native 的check 函数
1 check (this .mycontext , (String ) DexCall .callDexMethod (this .mycontext , this .mycontext .getString (R.string .dex ), this .mycontext .getString (R.string .classname ), this .mycontext .getString (R.string .func1 ), s));
在resources.arsc的 res/values/strings.xml
中可以看到对应的字符串
所以这个其实是
1 (String ) DexCall .callDexMethod ("zunjia.dex" ,"com.nobody.zundujiadu" , "encode" , s);
分析 check.so 文件
获取密文数据,ida 脚本
1 2 3 4 5 from idaapi import *for i in range (43 ): print (hex (get_bytes(0x3958 + i, 1 )[0 ]), end="," )
加密函数点进去看是 rc4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 import base64from itertools import product def rc4 (key, data ): """RC4算法实现""" S = list (range (256 )) j = 0 for i in range (256 ): j = (j + S[i] + key[i % len (key)]) % 256 S[i], S[j] = S[j], S[i] i = j = 0 result = bytearray () for byte in data: i = (i + 1 ) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] k = S[(S[i] + S[j]) % 256 ] result.append(byte ^ k &0xff ) return bytes (result) standard_base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" custom_base64 = "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5" encode_dict = {standard_base64[i]: custom_base64[i] for i in range (len (standard_base64))} def custom_base64_encode (data ): result = [] for i in range (len (data)): result.append(chr (ord (data[i]) ^ i)) data = '' .join(result) base64_encoded = base64.b64encode(data.encode('utf-8' )).decode('utf-8' ) return '' .join(encode_dict.get(c, c) for c in base64_encoded) encrypted_data = bytes ([ 0x7a , 0xc7 , 0xc7 , 0x94 , 0x51 , 0x82 , 0xf5 , 0x99 , 0x0c , 0x30 , 0xc8 , 0xcd , 0x97 , 0xfe , 0x3d , 0xd2 , 0xae , 0x0e , 0xba , 0x83 , 0x59 , 0x87 , 0xbb , 0xc6 , 0x35 , 0xe1 , 0x8c , 0x59 , 0xef , 0xad , 0xfa , 0x94 , 0x74 , 0xd3 , 0x42 , 0x27 , 0x98 , 0x77 , 0x54 , 0x3b , 0x46 , 0x5e , 0x95 ]) segments = ['o.0' , '0.o' ] for bits in product([0 , 1 ], repeat=12 ): password = '' .join([segments[b] for b in bits]) key=custom_base64_encode(password).encode() decrypted = rc4(key, encrypted_data) if b'hgame' in decrypted: print (decrypted)
flag 为 hgame{4af153b9-ed3e-420b-978c-eeff72318b49}