2026/4/6 7:25:52
网站建设
项目流程
专门做网页的网站,海外网站域名注册,做定制的网站,房屋中介的网站怎么建设EasyJNI
最近正好在出写JNI#xff0c;正好看到了一道JNI相关的较为简单明了的CTF#xff0c;就一时兴起的写了#xff0c;不得不说逆向工程和正向开发确实是可以互补互相加深的
JNI
JNI#xff08;Java Native Interface#xff09;即java本地接口#xff0c;众所周知正好看到了一道JNI相关的较为简单明了的CTF就一时兴起的写了不得不说逆向工程和正向开发确实是可以互补互相加深的JNIJNIJava Native Interface即java本地接口众所周知android有四层结构也有说五层结构即多了一个抽象层这里不予讨论应用层与应用接口层是用Java写的而C/C核心库和linux内核层由C/C写的既然知道了这一点那理解JNI就很简单了Java和C/C肯定是不能直接互相调用的那么应用层肯定就不能直接调用底层的东西比如从应用层直接用Java想调用底层C/C开发的启动相机或NFC等肯定是不能直接实现的。这就是JNI的作用了它充当桥梁向C/C转译Java中的方法向Java转译C/C的函数而NDK就是JNI开发工具。开始解题首先不考虑那么多的先AndroidKiller试试既然都AK(AndroidKiller)了就索性看看JADE实不相瞒的说在看到这两个东西之前我还想过会不会就是名字放着看的不是调用的JNI既然看到这里了就知道了确确实实调用了JNI那就一步一步的分析吧首先看OnCreate()就这一句话是有用的别的没啥用这段代码中设置了一个点击事件即创立了一个Button并通过ID找到Button然后设置SetOnClickListener设置监听点击之后调用上图中的a()方法传入的String是输入的String,通过a()方法返回一个布尔值那就回去看a方法就好了先放个整体图出来吧这样就能看到这题恶心人的地方了他的MainActivity中有a方法但是还有一个a类而且在a类中还有一个静态的Byte[]也叫a但没啥影响降维理解一下这里吧初始化一个a类locala并把输入进去的String类型的字符串穿换成Byte[]类型组传入a类locala的a方法。看着可能绕自己做的就还行了。那就看a类吧不知道是不是我jade版本的问题看这个a数组里边的东西明明是char型的东西没有自动转换成字符这就很烦了于是我换成了JEB1 package com.a.easyjni; 2 3 public class a { 4 private static final char[] a; 5 6 static { 7 a.a new char[]{i, 5, j, L, W, 7, S, 0, G, X, 6, u, f, 1, c, v, 3, n, y, 4, q, 8, e, s, 2, Q, , b, d, k, Y, g, K, O, I, T, /, t, A, x, U, r, F, l, V, P, z, h, m, o, w, 9, B, H, C, M, D, p, E, a, J, R, Z, N}; 8 } 9 10 public a() { 11 super(); 12 } 13 14 public String a(byte[] arg10) { 15 int v8 3; 16 StringBuilder v4 new StringBuilder(); 17 int v0; 18 for(v0 0; v0 arg10.length - 1; v0 3) { 19 byte[] v5 new byte[4]; 20 int v3 0; 21 byte v2 0; 22 while(v3 2) { 23 if(v0 v3 arg10.length - 1) { 24 v5[v3] ((byte)(v2 | (arg10[v0 v3] 0xFF) v3 * 2 2)); 25 v2 ((byte)(((arg10[v0 v3] 0xFF) (2 - v3) * 2 2 0xFF) 2)); 26 } 27 else { 28 v5[v3] v2; 29 v2 0x40; 30 } 31 32 v3; 33 } 34 35 v5[v8] v2; 36 int v2_1; 37 for(v2_1 0; v2_1 v8; v2_1) { 38 if(v5[v2_1] 0x3F) { 39 v4.append(a.a[v5[v2_1]]); 40 } 41 else { 42 v4.append(); 43 } 44 } 45 } 46 47 return v4.toString(); 48 } 49 }实不相瞒的说要不是我去看别人的WP我都没有意识到这是变种的Base64加密不过这个a作为密码表还是很明显的那再重新理一下逻辑输入了一个字符串Str这个字符串在a.a中被加密成立Str1,Str1被传到了布尔型的native层的ncheck中经过检测 并由返回的布尔值判断输出。那就简单了看ncheck就好了在ncheck中有判断需要逆向回去的字符串将apk解压so库就在lib下了IDA大法好CtrlF搜索ncheck就得到了想要的函数分析一下这个函数吧在v6之前说的都是废话不用管1 v6 (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 676))(a1, a3, 0); 2 if ( strlen(v6) 32 ) 3 { 4 v7 0; 5 do 6 { 7 v8 s1[v7]; 8 s1[v7] v6[v7 16]; 9 v9 v6[v7]; 10 v8[16] v9; 11 } 12 while ( v7 ! 16 ); 13 (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v4 680))(v4, v5, v6); 14 v10 0; 15 do 16 { 17 v12 __OFSUB__(v10, 30); 18 v11 v10 - 30 0; 19 v16 s1[v10]; 20 s1[v10] s1[v10 1]; 21 s1[v10 1] v16; 22 v10 2; 23 } 24 while ( v11 ^ v12 ); 25 v13 memcmp(s1, MbT3sQgX039i3gAQOoMQFPskB1Bsc7, 0x20u); 26 result 0; 27 if ( !v13 ) 28 result 1; 29 } 30 else 31 { 32 (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v4 680))(v4, v5, v6); 33 result 0; 34 } 35 return result;不管别的至少我第一眼看到的就是“MbT3sQgX039i3gAQOoMQFPskB1Bsc7”在这我大概就知道是Base64了这个两个等号太明显了而且前面的密码表很明显跟base64不一样那就确定是base64的变种了。在这个函数中也有加密先看一下这个加密吧两个do…while先看两个while一个是v7由0变到16一个是计算一个异或值。先看第一个do…while就是将前16位与后16位交换位置没啥好多说的看看第二个do…whileV12是一个判断溢出OFSUB(int a,int b)经过查询之后的作用是判断a-b是否会产生溢出即a(-b)是否溢出-30补码为11100010,如果要产生溢出那最小的补码为00100000十进制就是16跟前面那个就有点意思了也是16。并且在0到16期间也不可能减去30大于等于0即V11永远为0。那么V11^V12的第一个跳出条件就是当V10为16的时候。也能看出来作用就是两两交换位置。“v13 memcmp(s1, “MbT3sQgX039i3gAQOoMQFPskB1Bsc7”, 0x20u)”这里memcmp(String str1,String str2,int n)作用判断为判断str1与str2的前n位而0x20就是32。从吾爱白嫖来的解密脚本#白嫖来的base64解密函数 base64_charset i5jLW7S0GX6uf1cv3ny4q8es2QbdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN def decode(base64_str): base64_bytes [{:06}.format(str(bin(base64_charset.index(s))).replace(0b, )) for s in base64_str if s ! ] resp bytearray() nums len(base64_bytes) // 4 remain len(base64_bytes) % 4 integral_part base64_bytes[0:4 * nums] while integral_part: tmp_unit .join(integral_part[0:4]) tmp_unit [int(tmp_unit[x: x 8], 2) for x in [0, 8, 16]] for i in tmp_unit: resp.append(i) integral_part integral_part[4:] if remain: remain_part .join(base64_bytes[nums * 4:]) tmp_unit [int(remain_part[i * 8:(i 1) * 8], 2) for i in range(remain - 1)] for i in tmp_unit: resp.append(i) return resp #两两交换位置还原so内的加密 flag AQOoMQFPskB1Bsc7MbT3sQgX039i3g tmp for i in range(len(flag)//2): tmp flag[i*21] flag[i*2] print(tmp) print(decode(tmp))