攻防世界mobile练习区easyjni

246 1
小菜鸟一枚 2022-8-10 13:12:15 | 显示全部楼层 |阅读模式
## 0x1 静态分析
  1.将程序下载下来,发现是个apk文件,那就用jadx工具打开看看,先看看主函数,看oncreate函数,一般这就是启动函数了,可以看到程序逻辑就是调用m1a方法,根据返回值决定成功还是失败。

  1.         public void onCreate(Bundle bundle) {
  2.         super.onCreate(bundle);
  3.         setContentView(R.layout.activity_main);
  4.         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { // from class: com.a.easyjni.MainActivity.1
  5.             @Override // android.view.View.OnClickListener
  6.             public void onClick(View view) {
  7.                 if (MainActivity.this.m1a(((EditText) ((MainActivity) this).findViewById(R.id.edit)).getText().toString())) {
  8.                     Toast.makeText(this, "You are right!", 1).show();
  9.                 } else {
  10.                     Toast.makeText(this, "You are wrong! Bye~", 1).show();
  11.                 }
  12.             }
  13.         });
  14.     }
复制代码




  2.再来看m1a方法,发现是new了一个C0678类对象,然后调用m0a方法得到一个返回值,然后通过ncheck去验证。

  1.     public boolean m1a(String str) {
  2.         try {
  3.             return ncheck(new C0678a().m0a(str.getBytes()));
  4.         } catch (Exception e) {
  5.             return false;
  6.         }
  7.     }
复制代码



  3. 再去看看C0678a这个类,创建类对象时会初始化一个静态字符串f2481a,再看m0a方法,接收一个前面我们在文本框输入的字符串,然后很像base64编码的过程,左右移位,f2481a就是码表,这里仔细看可以看到for循环对我们的字符串进行移位,每三位一组处理成4个字符,最后不足的末尾补=号,转换后的字符串存在sb中,然后返回这个字符串。

  1. public class C0678a {

  2.     /* renamed from: a */
  3.     private static final char[] f2481a = {'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'};

  4.     /* renamed from: a */
  5.     public String m0a(byte[] bArr) {
  6.         StringBuilder sb = new StringBuilder();
  7.         for (int i = 0; i <= bArr.length - 1; i += 3) {
  8.             byte[] bArr2 = new byte[4];
  9.             byte b = 0;
  10.             for (int i2 = 0; i2 <= 2; i2++) {
  11.                 if (i + i2 <= bArr.length - 1) {
  12.                     bArr2[i2] = (byte) (b | ((bArr[i + i2] & 255) >>> ((i2 * 2) + 2)));
  13.                     b = (byte) ((((bArr[i + i2] & 255) << (((2 - i2) * 2) + 2)) & 255) >>> 2);
  14.                 } else {
  15.                     bArr2[i2] = b;
  16.                     b = 64;
  17.                 }
  18.             }
  19.             bArr2[3] = b;
  20.             for (int i3 = 0; i3 <= 3; i3++) {
  21.                 if (bArr2[i3] <= 63) {
  22.                     sb.append(f2481a[bArr2[i3]]);
  23.                 } else {
  24.                     sb.append('=');
  25.                 }
  26.             }
  27.         }
  28.         return sb.toString();
  29.     }
  30. }
复制代码



&#8195;&#8195;4.返回值看明白了,现在去看ncheck方法是怎么验证的,跟过去之后发现是一个native方法,那就只能换IDA继续跟了,找到so文件用IDA打开,搜索java,找到了ncheck函数,有些看不懂这些赋值操作,不要晕,先看返回值,result如果是0那肯定就是失败了,所以result必须是1,然后往回看到memcmp比较函数,也就是说v12必须等于"MbT3sQgX039i3g==AQOoMQFPskB1Bsc7"。

  1. v5 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
  2.   if ( strlen(v5) == 32 )
  3.   {
  4.     for ( i = 0; i != 16; ++i )
  5.     {
  6.       v7 = &v12[i];
  7.       v12[i] = v5[i + 16];
  8.       v8 = v5[i];
  9.       v7[16] = v8;
  10.     }
  11.     (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)a1 + 680))(a1, a3, v5);
  12.     v9 = 0;
  13.     do
  14.     {
  15.       v10 = v9 < 30;
  16.       v13 = v12[v9];
  17.       v12[v9] = v12[v9 + 1];
  18.       v12[v9 + 1] = v13;
  19.       v9 += 2;
  20.     }
  21.     while ( v10 );
  22.     result = memcmp(v12, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", 0x20u) == 0;
  23.   }
  24.   else
  25.   {
  26.     (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)a1 + 680))(a1, a3, v5);
  27.     result = 0;
  28.   }
  29.   return result;
  30. }
复制代码



&#8195;&#8195;5.现在看看v12,32位的字符串,现在假设v5就是我们输入的字符串编码后的形式,这段代码就好理解了,分为两个循环,第一个for循环就是将v5的前16位与后16位对调赋值给v12。



&#8195;&#8195;6.再看第二个do while循环v9每次自增2,把中间三行挑出来单独看,这不就是典型的交换两个变量的值嘛,那这里就是每次循环取v12中的两个值,然后交换,最终和"MbT3sQgX039i3g==AQOoMQFPskB1Bsc7"比较,相等,就说明验证通过了。

  1. v13 = v12[v9];
  2. v12[v9] = v12[v9 + 1];
  3. v12[v9 + 1] = v13;
复制代码



## 0x2 动态分析
&#8195;&#8195;1.运行一下程序,用JEB动态调试看看,发现报错了,原来是上次把root权限给关了造成的,重新打开即可。



&#8195;&#8195;2.CTRL+B在jeb中打上断点,将apk拖进jeb,然后在输入123456点check按钮,断了下来,直接运行到函数末尾,看右侧变量,可以知道我输入的123456,经过base64编码得到:"f4G91LqC"。

(

&#8195;&#8195;3.在打开IDA调试so文件,回忆一下步骤adb shell,mount -o rw,remount /,重新挂载一下文件系统,adb push android_x86_server /sbin ,chmod +x /sbin/android_x86_server,给它可执行权限,然后输入android_x86_server,运行起来监听23946端口,adb forward tcp:23946 tcp:23946命令,将手机上的23946窗口,转发到我们电脑本地的23946端口。

&#8195;&#8195;4.然后一顿操作下来,懵了,不知道咋调试,本来还想验证下v5是不是我想的那样,这只能算了。

## 0x3 计算flag
&#8195;&#8195;1.把"MbT3sQgX039i3g==AQOoMQFPskB1Bsc7"作为结果反推。

&#8195;&#8195;2.还原so第二个循环do while,每两个变量前后位置交换

&#8195;&#8195;3.还原so第一个循环for,交换前16位和后16位

&#8195;&#8195;4.编写base64解码程序,直接把之前自己实现的c++版搬运过来即可。

&#8195;&#8195;5.得到最终的解密代码:

  1. package ctf;

  2. import java.util.Base64;

  3. public class test01 {

  4.         public static void main(String[] args) {

  5.                 char[] mb = { 'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v', '3', 'n', 'y',
  6.                                 '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x',
  7.                                 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h', 'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J',
  8.                                 'R', 'Z', 'N' };
  9.                 String output = "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7";
  10.                 StringBuffer flag = new StringBuffer("");
  11.                 StringBuffer f_flag = new StringBuffer("");

  12.                 int i = 0;

  13.                 // 每两个变量交换前后的值
  14.                 do {
  15.                         flag.append(output.charAt(i + 1));
  16.                         flag.append(output.charAt(i));
  17.                         i += 2;
  18.                 } while (i <= 30);

  19.                 System.out.println("flag1:" + flag);

  20.                 // 交换前16位和后16位
  21.                 for (i = 0; i != 16; ++i) {
  22.                         char temp = flag.charAt(i);
  23.                         flag.setCharAt(i, flag.charAt(i + 16));
  24.                         flag.setCharAt(i + 16, temp);
  25.                 }

  26.                 System.out.println("flag2:" + flag);

  27.                 // base64解码得到flag
  28.                 i = 0;// 密文下标

  29.                 byte[] b_flag = flag.toString().getBytes();
  30.                 while (i < b_flag.length)// 判断是否解密完毕
  31.                 {
  32.                         int index = 0;// 码表下标
  33.                         int temp1 = -1;// 保存第一个密文下标
  34.                         int temp2 = -1;// 保存第二个密文下标
  35.                         int temp3 = -1;// 保存第三个密文下标
  36.                         int temp4 = -1;// 保存第四个密文下标

  37.                         // 通过码表反查密文对应的下标,然后分别获得十进制数字
  38.                         for (; index < 64; index++) {
  39.                                 if (b_flag[i] == mb[index]) {
  40.                                         temp1 = index;
  41.                                 }
  42.                                 if (b_flag[i + 1] == mb[index]) {
  43.                                         temp2 = index;
  44.                                 }
  45.                                 if (b_flag[i + 2] == mb[index]) {
  46.                                         temp3 = index;
  47.                                 }
  48.                                 if (b_flag[i + 3] == mb[index])// 一轮密文4个字节已取出,退出循环
  49.                                 {
  50.                                         temp4 = index;
  51.                                 }

  52.                                 // 如果已经查到四个下标就退出循环!
  53.                                 if (temp1 != -1 && temp2 != -1 && temp3 != -1 && temp4 != -1)
  54.                                         break;
  55.                         }

  56.                         if (temp3 != -1 && temp4 != -1)// 完整的读取到了4个字节,直接解密
  57.                         {
  58.                                 f_flag.append((char) ((temp1 << 2) | (temp2 >> 4)));// 取第一个密文6个字符再加上第二个密文前2个字符
  59.                                 f_flag.append((char) (((temp2 & 15) << 4) | (temp3 >> 2))); // 取第二个密文后4个字符再加上第三个密文前4个字符
  60.                                 f_flag.append((char) (((temp3 & 3) << 6) | temp4)); // 取第三个密文后2个字符再加上第四个密文6个字符
  61.                         } else if (temp3 == -1)// 只取到了2个字节
  62.                         {
  63.                                 f_flag.append((char) ((temp1 << 2) | (temp2 >> 4))); // 取第一个密文6个字符再加上第二个密文前2个字符
  64.                         } else if (temp4 == -1)// 只取到了3个字节
  65.                         {
  66.                                 f_flag.append((char) ((temp1 << 2) | (temp2 >> 4)));// 取第一个密文6个字符再加上第二个密文前2个字符
  67.                                 f_flag.append((char) ((temp2 & 15) << 4 | (temp3 >> 2)));// 取第二个密文后4个字符第三个密文前4个字符
  68.                         } else {
  69.                                 System.out.println("下标取值不对,程序逻辑错误!!!");
  70.                         }
  71.                         i += 4;// 密文每次循环向后移动4位
  72.                 }
  73.                 System.out.println("flag3:" + f_flag);

  74.         }
  75. }
复制代码



&#8195;&#8195;6.运行,得到flag:“flag{just_ANot#er_@p3}”。

  1. flag1:bM3TQsXg30i9g3==QAoOQMPFks1BsB7c
  2. flag2:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
  3. flag3:flag{just_ANot#er_@p3}
复制代码

胡h 2022-9-4 18:37:47 | 显示全部楼层
虽然看不懂太多 但是已经打开了 就必须看完
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行