CTF题GoNative破解过程记录
我对native部分真的当前了解的非常少,必须从现在起抓紧时间给它一顿暴学。
本次破解的是一到CTF题,找到正确的Flag即可。
Apk下载地址:链接:https://pan.baidu.com/s/1K6wauXK73pzO4wpueCWV9A
提取码:qola
jni.h下载地址:链接:https://pan.baidu.com/s/1DSlUORCsZVcxbvfzaJTUYg
提取码:psxs
1.老规矩吧,先拖到jeb里,给它一顿反编译。由于只有一个界面,直接查看MainActivity即可。
2.最终填入的String应该是放到FlagChecker.checkFlag方法中进入校验。查看checkFlag方法。
3.这段很容易懂,正确的字符串大体上应该长成这个屌样:“MOBISEC{X1-X2}" 。 其中X2部分是由数字组成,长度为6。最终将X1字符串和转成int值的X2传入到native方法中。
首先找到so,armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年15月以后的生产的大部分Android设备都使用它.
arm64-v8a: 第8代、64位ARM处理器,很少设备,三星 Galaxy S6是其中之一。
armeabi: 第5代、第6代的ARM处理器,早期的手机用的比较多。
x86: 平板、模拟器用得比较多。
x86_64: 64位的平板。
到apk中对应的位置找到native-lib.so然后拖到IDA中一路确定,找到helloFromTheOtherSide方法,按F5查看伪代码。
bool __fastcall Java_com_mobisec_gonative_FlagChecker_helloFromTheOtherSide(int a1, int a2, int a3, int a4) { int v4; // r6 int v5; // r4 int v6; // r8 const char *v7; // r0 char *v8; // r5 size_t v9; // r0 bool v10; // zf _BOOL4 result; // r0 int v12; // r0 bool v13; // zf int v14; // r0 bool v15; // zf int v16; // r0 bool v17; // zf int v18; // r4 char dest; // [sp+6h] [bp-22h] char v20; // [sp+Bh] [bp-1Dh] int v21; // [sp+Ch] [bp-1Ch] v4 = a1; v5 = a4; v6 = a3; v7 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0); v8 = (char *)v7; v9 = strlen(v7); v10 = v9 == 12; if ( v9 == 12 ) v10 = v5 == 31337; if ( v10 ) goto LABEL_6; LABEL_4: (*(void (__fastcall **)(int, int, char *))(*(_DWORD *)v4 + 680))(v4, v6, v8); result = 0; while ( _stack_chk_guard != v21 ) { LABEL_6: v12 = (unsigned __int8)*v8; v13 = v12 == 110; if ( v12 == 110 ) v13 = v8[11] == 111; if ( !v13 ) goto LABEL_4; strncpy(&dest, v8 + 1, 5u); v20 = 0; if ( strncmp("ative", &dest, 5u) ) goto LABEL_4; v14 = (unsigned __int8)v8[9]; v15 = v14 == 95; if ( v14 == 95 ) v15 = v8[6] == 95; if ( !v15 ) goto LABEL_4; v16 = (unsigned __int8)v8[7]; v17 = v16 == 105; if ( v16 == 105 ) v17 = v8[8] == 115; if ( !v17 ) goto LABEL_4; v18 = strcmp("so", v8 + 10); (*(void (__fastcall **)(int, int, char *))(*(_DWORD *)v4 + 680))(v4, v6, v8); result = v18 == 0; } return result; }
4.这是什么鸟玩意呀,C语言里的指针这个星那个星的,看的我到是满眼的星。传进来两个参数怎么给我整成4个int了?
首先要知道,在JNI中本地方法第一个参数是JNIEnv接口指针,指向一个个函数表,函数表中的每一个入口指向一个JNI函数。
第二个参数根据本地方法是一个静态方法还是实例方法而有所不同。本地方法是一个静态方法时,第二个参数代表本地方法所在的类;本地方法是一个实例方法时,第二个参数代表本地方法所在的对象。
其次,要导入jni.h的函数库,点File->Load file->Parse C header file 找到jni.h所在位置点打开,此时显示Compilation successful 导入成功。
点击函数第一个参数int 右键Convert to struct* 在弹出的框中选择_JNIEnv点ok。
将已经显示出来一些函数,选中右键Force call type。
然后点住第一个参数a1按住n键重命名为env。
1 bool __fastcall Java_com_mobisec_gonative_FlagChecker_helloFromTheOtherSide(_JNIEnv *env, int a2, int a3, int a4) 2 { 3 _JNIEnv *v4; // r6 4 int v5; // r4 5 void *v6; // r8 6 const char *v7; // r0 7 const char *v8; // r5 8 size_t v9; // r0 9 bool v10; // zf 10 _BOOL4 result; // r0 11 int v12; // r0 12 bool v13; // zf 13 int v14; // r0 14 bool v15; // zf 15 int v16; // r0 16 bool v17; // zf 17 int v18; // r4 18 char dest; // [sp+6h] [bp-22h] 19 char v20; // [sp+Bh] [bp-1Dh] 20 int v21; // [sp+Ch] [bp-1Ch] 21 22 v4 = env; 23 v5 = a4; 24 v6 = (void *)a3; 25 v7 = env->functions->GetStringUTFChars(&env->functions, (jstring)a3, 0); //把一个jstring指针(指向JVM内部的Unicode字符序列)
转化成一个UTF-8格式的C字符串 26 v8 = v7; 27 v9 = strlen(v7); //获取字符串长度 28 v10 = v9 == 12; 29 if ( v9 == 12 ) 30 v10 = v5 == 31337; 31 if ( v10 ) 32 goto LABEL_6; 33 LABEL_4: 34 v4->functions->ReleaseStringUTFChars(&v4->functions, v6, v8); //从GetStringUTFChars中获取的UTF-8字符串在本地代码中使用完毕后,
要使用ReleaseStringUTFChars告诉JVM这个UTF-8字符串不会被使用了,
因为这个UTF-8字符串占用的内存会被回收。 35 result = 0; 36 while ( _stack_chk_guard != v21 ) 37 { 38 LABEL_6: 39 v12 = *(unsigned __int8 *)v8; 40 v13 = v12 == 110; //”n“ 41 if ( v12 == 110 ) 42 v13 = v8[11] == 111; //”o“ 43 if ( !v13 ) 44 goto LABEL_4; 45 strncpy(&dest, v8 + 1, 5u); 46 v20 = 0; 47 if ( strncmp("ative", &dest, 5u) ) 48 goto LABEL_4; 49 v14 = *((unsigned __int8 *)v8 + 9); 50 v15 = v14 == 95; //”_" 51 if ( v14 == 95 ) 52 v15 = v8[6] == 95; 53 if ( !v15 ) 54 goto LABEL_4; 55 v16 = *((unsigned __int8 *)v8 + 7); 56 v17 = v16 == 105; //“i” 57 if ( v16 == 105 ) 58 v17 = v8[8] == 115; //“s” 59 if ( !v17 ) 60 goto LABEL_4; 61 v18 = strcmp("so", v8 + 10); 62 v4->functions->ReleaseStringUTFChars(&v4->functions, v6, v8); 63 result = v18 == 0; 64 } 65 return result; 66 }
5.是不是读起来好接受多了,从这段代码中就能分析出来,先判断传进来的X1长度是不是12,再看X2是不是等于31337,由此可得后半段字符串应为”031337}“。
c语言中对字符比较只能比较其ascii码,所以下面的步骤是先比较第一位和最后一位是不是“n”和“o”。是的话看第2到第6位是不是“ative”。
再看第10位和第7位是不是“_",再看第8第9位是不是”i“和”s“,最后比较最后两位是不是”so“。所以前半段应该为”MOBISEC{native_is_so”。最终结果为“MOBISEC{native_is_so-031337}”。
6.破解完后如下图所示,有一说一,这个so里的逻辑很简单,非常适合我这种刚刚起步的小老弟。今后还是要把这个逆向给它一顿臭学。
参考资料: https://www.52pojie.cn/thread-742703-1-1.html (推荐)