android-app-100 suctf-2016
jeb启动,找到点击事件:
验证流程:
输入作为参数 --> processObjectArrayFromNative 得到一返回值(ret_a) --> IsCorrect 返回0,失败;返回1,成功 --> 输出"Sharif_CTF("+md5(str(d+ret_a)+” “+”)“ (.d = 0x1BEBE)
可以发现flag的获取关键在于processObjectArrayFromNative 的返回值。
1 public void onClick(View arg8) { 2 new String(" "); 3 String v0 = this.a.b.getText().toString(); 4 Log.v("EditText", this.a.b.getText().toString()); 5 new String(""); 6 int v1 = this.a.processObjectArrayFromNative(v0); 7 int v2 = this.a.IsCorrect(v0); 8 v0 = String.valueOf(this.a.d + v1) + " "; 9 try { 10 MessageDigest v1_1 = MessageDigest.getInstance("MD5"); 11 v1_1.update(v0.getBytes()); 12 byte[] v1_2 = v1_1.digest(); 13 StringBuffer v3 = new StringBuffer(); 14 int v0_2; 15 for(v0_2 = 0; v0_2 < v1_2.length; ++v0_2) { 16 v3.append(Integer.toString((v1_2[v0_2] & 0xFF) + 0x100, 16).substring(1)); 17 } 18 19 if(v2 == 1 && this.a.e != "unknown") { 20 this.a.c.setText("Sharif_CTF(" + v3.toString() + ")"); 21 } 22 23 if(v2 == 1 && this.a.e == "unknown") { 24 this.a.c.setText("Just keep Trying :-)"); 25 } 26 27 if(v2 == 0) { 28 this.a.c.setText("Just keep Trying :-)"); 29 } 30 31 return; 32 } 33 catch(NoSuchAlgorithmException v0_1) { 34 v0_1.printStackTrace(); 35 return; 36 } 37 }
程序有两个native方法:
public native int IsCorrect(String arg1)
public native int processObjectArrayFromNative(String arg1)
IDA启动,
发现有混淆,但我们还是能发现这两个native方法中调用了strcmp方法
Java_com_example_ctf2_MainActivity_processObjectArrayFromNative:
1 ptr_chars = (env_2->functions->GetStringUTFChars)(env_2, jstring_2, 0); 2 *p_chars = ptr_chars; 3 temp_chars_ptr = *p_chars; 4 v46 = &v8; 5 v51 = 101; 6 v8 = 926246501; 7 v52 = 53; 8 v45 = 55; 9 v9 = 102; 10 v10 = 51; 11 v11 = 102; 12 v12 = 101; 13 v13 = 51; 14 v44 = 99; 15 v14 = 99; 16 v15 = 102; 17 v16 = 54; 18 v17 = 48; 19 v18 = 51; 20 v19 = 99; 21 v20 = 48; 22 v21 = 51; 23 v22 = 56; 24 v23 = 57; 25 v24 = 48; 26 v25 = 101; 27 v26 = 101; 28 v27 = 53; 29 v28 = 56; 30 v29 = 56; 31 v30 = 56; 32 v31 = 55; 33 v32 = 56; 34 v33 = 99; 35 v34 = 48; 36 v35 = 101; 37 v36 = 99; 38 v50_2 = v50; 39 v38 = 53; 40 v5 = j_strcmp(temp_chars_ptr, &v8);
processObjectArrayFromNative方法返回值:0或者0x57cbbd2
Java_com_example_ctf2_MainActivity_IsCorrect方法内也进行了字符串比较,
通过脚本我们可以获取到进行比较的字符串:
1 v51 = ‘e‘; 2 v8 = ‘75fe‘[::-1]; 3 v52 = ‘5‘; 4 v45 = ‘7‘; 5 v9 = ‘f‘; 6 v10 = ‘3‘; 7 v11 = ‘f‘; 8 v12 = ‘e‘; 9 v13 = ‘3‘; 10 v44 = ‘c‘; 11 v14 = ‘c‘; 12 v15 = ‘f‘; 13 v16 = ‘6‘; 14 v17 = ‘0‘; 15 v18 = ‘3‘; 16 v19 = ‘c‘; 17 v20 = ‘0‘; 18 v21 = ‘3‘; 19 v22 = ‘8‘; 20 v23 = ‘9‘; 21 v24 = ‘0‘; 22 v25 = ‘e‘; 23 v26 = ‘e‘; 24 v27 = ‘5‘; 25 v28 = ‘8‘; 26 v29 = ‘8‘; 27 v30 = ‘8‘; 28 v31 = ‘7‘; 29 v32 = ‘8‘; 30 v33 = ‘c‘; 31 v34 = ‘0‘; 32 v35 = ‘e‘; 33 v36 = ‘c‘; 34 x=‘‘ 35 for i in range(8,37): 36 x+=locals()[‘v‘+str(i)] 37 print(x) 38 print(len(x)) 39 40 v12 = 101; 41 v13 = 102; 42 v14 = 53; 43 v15 = 55; 44 v16 = 102; 45 v17 = 51; 46 v18 = 102; 47 v19 = 101; 48 v20 = 51; 49 v21 = 99; 50 v22 = 102; 51 v23 = 54; 52 v24 = 48; 53 v25 = 51; 54 v26 = 99; 55 v27 = 48; 56 v28 = 51; 57 v29 = 56; 58 v30 = 57; 59 v31 = 48; 60 v32 = 101; 61 v33 = 101; 62 v34 = 53; 63 v35 = 56; 64 v36 = 56; 65 v37 = 56; 66 v38 = 55; 67 v39 = 56; 68 v40 = 99; 69 v41 = 48; 70 v42 = 101; 71 v43 = 99; 72 73 y=‘‘ 74 for i in range(12,44): 75 y+=chr(locals()[‘v‘+str(i)]) 76 print(y) 77 print(len(y)) 78 79 80 ‘‘‘ 81 ef57f3fe3cf603c03890ee588878c0ec 82 32 83 ef57f3fe3cf603c03890ee588878c0ec 84 32 85 ‘‘‘
ef57f3fe3cf603c03890ee588878c0ec
运行adb命令输入到编辑框
adb shell input text ef57f3fe3cf603c03890ee588878c0ec
当然也可以静态获取:
1 ret_a=0x57CBBD2 2 d=0x1BEBE 3 d=str(d+ret_a)+‘ ‘ 4 print(d) 5 import hashlib 6 m=hashlib.md5(d.encode()).hexdigest() 7 print(‘Sharif_CTF(‘+m+‘)‘)
Sharif_CTF(833489ef285e6fa80690099efc5d9c9d)
(一开始还原算法时得到的结果不对,又写了个frida脚本验证,返回值没问题,后来才发现要md5的字符串忘了加空格 0.0)
frida脚本:
1 import frida, sys 2 3 4 def on_message(message, data): 5 if message[‘type‘] == ‘send‘: 6 print("[*] {0}".format(message[‘payload‘])) 7 else: 8 print(message) 9 10 11 jscode = """ 12 setImmediate(function () { 13 Java.perform(function () { 14 console.log("start"); 15 //so层hook 16 //导出函数 17 //var exports = Module.enumerateExportsSync("libadnjni.so"); 18 //for(var i=0;i<exports.length;i++){ 19 // send("name:"+exports[i].name+" address:"+exports[i].address); 20 // } 21 var str = Java.use("java.lang.String"); 22 //遍历模块找基址 23 // Process.enumerateModules({ 24 // onMatch: function (exp) { 25 // if (exp.name == ‘libadnjni.so‘) { 26 // send(‘enumerateModules find‘); 27 // send(exp.name + "|" + exp.base + "|" + exp.size + "|" + exp.path); 28 // send(exp); 29 // return ‘stop‘; 30 // } 31 // }, 32 // onComplete: function () { 33 // send(‘enumerateModules stop‘); 34 // } 35 // }); 36 37 //通过模块名直接查找基址 38 var soAddr = Module.findBaseAddress("libadnjni.so"); 39 send("soAddr:" + soAddr); 40 41 var parray=0x48c+1; 42 var pcorrect=0x74c+1; 43 // hook导出函数 通过函数名 44 45 //Module.findExportByName 找到的函数地址无效 46 // var farray=Module.findExportByName("libadnjni.so", "Java_com_example_ctf2_MainActivity_processObjectArrayFromNative") 47 // send("findExportByName farray() by Module.findExportByName:" +farray); 48 var farray=new NativePointer(soAddr).add(parray); 49 //NativePointer 简写ptr 50 send("findExportByName farray() by ptr:" +farray ); 51 52 Interceptor.attach(farray, { 53 onEnter: function (args) { 54 var s = Java.cast(args[2], str); 55 send("array() jstring:" + s ); 56 }, 57 onLeave: function (retval) { 58 send("array() return:" + retval); 59 } 60 }); 61 62 // hook导出函数 通过函数名 63 // var fcorrect=Module.findExportByName("libadnjni.so", "Java_com_example_ctf2_MainActivity_IsCorrect"); 64 // send("findExportByName correct() by Module.findExportByName:" +fcorrect ); 65 var fcorrect=new NativePointer(soAddr).add(pcorrect); 66 send("findExportByName correct() by ptr:" +fcorrect ); 67 Interceptor.attach(fcorrect, { 68 onEnter: function (args) { 69 var s = Java.cast(args[2], str); 70 send("fcorrect() jstring:" + s ); 71 }, 72 onLeave: function (retval) { 73 send("fcorrect() return:" + retval); 74 } 75 }); 76 77 }); 78 }); 79 """ 80 # print(jscode) 81 82 # 启动时hook 83 # devices=frida.get_usb_device() 84 # pid=devices.spawn([‘com.example.goal‘]) 85 # session=devices.attach(pid) 86 # devices.resume(pid) #创建完脚本, 恢复进程运行 87 # script=session.create_script(jscode) 88 89 # 命令行frida -U -f com.example.goal --no-pause -l <hook.js> 90 91 # 运行中hook 92 process = frida.get_usb_device().attach(‘com.example.ctf2‘) 93 script = process.create_script(jscode) 94 script.on(‘message‘, on_message) 95 print(‘[*] Running test‘) 96 script.load() 97 sys.stdin.read() 98 99 # ef57f3fe3cf603c03890ee588878c0ec 100 101 ‘‘‘ 102 [*] Running test 103 start 104 [*] soAddr:0xcd562000 105 [*] findExportByName farray() by ptr:0xcd56248d 106 [*] findExportByName correct() by ptr:0xcd56274d 107 [*] array() jstring:Serial Number 108 [*] array() return:0x0 109 [*] fcorrect() jstring:Serial Number 110 [*] fcorrect() return:0x0 111 112 113 [*] array() jstring:ef57f3fe3cf603c03890ee588878c0ec 114 [*] array() return:0x57cbbd2 115 [*] fcorrect() jstring:ef57f3fe3cf603c03890ee588878c0ec 116 [*] fcorrect() return:0x1 117 ‘‘‘