zoukankan      html  css  js  c++  java
  • NJCTF (easycrack)

    安装app查看。一个输入框,输入随便输入显示Try again.

    放入JEB反编译。

    关于输入框监听是第一次见,具体可以看看这个博客https://www.jianshu.com/p/f976c677189a

    函数的作用是,如果字符串s变动,将会执行具体的实现。这里是调用了native层的parseText函数。

    接下来反编译apk,用IDA打开位于lib文件夹中的so文件,如下图所示。

     具体关于JNI,可以看这个博客,https://blog.csdn.net/smbroe/article/details/44133741

    jni方法有两种映射:

    动态注册的native方法,必须实现JNI_OnLoad方法,同时实现一个JNINativeMethod[]数组, 实现Java方法与native方法的映射绑定 
    静态注册的native方法,必须是以Java+类完整路径+方法名的格式来命名native方法。

    这里是静态注册的,首先要选中这里的a1,按Y,改成下图所示,这样IDA才能够正确分析。

    返回Java层,查看messageMe函数。函数很简单,直接赋值java包的名字,复制黏贴代码到java编译器中跑出即可。

     1 public class easycrack100 {
     2     public static void main(String[] args) {
     3         String v3 = "";
     4         int v4 = 51;
     5         String[] v1 = "com.njctf.mobile.easycrack".split("\\.");
     6         /*1、如果用“.”作为分隔的话,必须是如下写法,String.split("\\."),这样才能正确的分隔开,不能用String.split(".");
     7         2、如果用“|”作为分隔的话,必须是如下写法,String.split("\\|"),这样才能正确的分隔开,不能用String.split("|");
     8         “.”和“|”都是转义字符,必须得加"\\";*/
     9         char[] v6 = v1[v1.length - 1].toCharArray();
    10         int v7 = v6.length;
    11         int v5;
    12         for(v5 = 0; v5 < v7; ++v5) {
    13             v4 ^= v6[v5];
    14             v3 += ((char)v4);
    15         }
    16         System.out.println(v3);
    17     }
    18 }

    message='V7D=^,M.E'

    返回IDA继续分析。

    这个循环也容易理解,异或后的字符串放入了v23。再往下。

    打开init函数。

    稍微改一下放到c编译器中得到key数组。

    #include<stdio.h>
    using namespace std;
    int main()
    {
      string a2 = "I_am_the_key";
      string v11 = a2;
      int res[256];
      int v3 = 256;
      int v14[256];
      int *v8;
      int v4 = 0;
      int v5 = 0;
      int *v7;
      int i = 0;
      do
      {
        res[v5] = v5;                               
        v14[v5++] = v11[i];
        i++;
        i%=12;
      }
      while ( v5 != 256 );
      v7=v14;
      v8 = res;
      int v9;
      do
      {
        v9 = *v8;
        v4 = (v9 + v4 + *v7) % 256;
        *v8 = res[v4];
        res[v4] = v9;
        --v3;
        ++v7;
        ++v8;
      }
      while ( v3 );
      for(int i=0;i<256;i++) printf("0x%02x,",res[i]);
      return 0;
    }

    再往下,将得到key和异或后的字符串进行加密。

    因为我们要逆向得到正确的输入(即flag),所以需要知道经过整个加密过程后的对比字符串。

    这里双击compare就能看到了。这是一串十六进制。

    在它的上面,还有一步,就是把经过crypt后的字符串转成16进制,如下图。

    下面画个流程图,梳理一下思路。

    最后跟据crypt函数写出脚本即可。

    #include<iostream>
    using namespace std;
    int main()
    {
        int a3 = 30;
        int v3 = 0;
        int v4 = 0;
        int v5;
        int i = 0;
        int a2[32] = {0xC8,0xE4,0xEF,0x0E,0x4D,0xCC,0xA6,0x83,0x08,0x81,0x34,0xF8,0x63,0x5E,0x97,0x0E,0xEA,0xD9,0xE2,0x77,0xF3,0x14,0x86,0x9F,0x7E,0xF5,0x19,0x8A,0x2A,0xA4};
        int result[256] = {0x39,0xa9,0x72,0x2d,0xe8,0x58,0x26,0x32,0x81,0xd,0xac,0x49,0xbb,0x10,0x46,0x65,0xb3,0x92,0xf,0x84,0xb8,0xbf,0xf2,0x52,0xe3,0x5b,0xfc,0xd5,0x59,0x6a,0xf0,0x5d,0x60,0x69,0x16,0x8e,0xfb,0x94,0x48,0xbc,0x71,0x36,0x57,0xad,0x44,0x7c,0x95,0xda,0xb7,0x47,0xdb,0x35,0x3c,0xd2,0x23,0xc5,0xa8,0xb,0x9f,0x31,0xd8,0x1f,0x3f,0xb0,0x2e,0xe1,0x5a,0x4a,0xf9,0x1,0x54,0xa7,0xa5,0xee,0x8,0x99,0x63,0x9b,0x50,0xbd,0x5,0xf7,0xcb,0xab,0x22,0xc2,0x8a,0x38,0x7d,0x6,0xb1,0xc0,0x4e,0x74,0x3a,0xe5,0x67,0x2b,0xa3,0x73,0x89,0x9e,0xba,0x88,0x3d,0x28,0x62,0x8f,0xfd,0x43,0x98,0x4d,0x56,0xb2,0xc,0x29,0x6e,0x78,0x25,0xe0,0xe9,0xf6,0x9c,0x13,0xed,0xf8,0xc4,0x20,0x87,0x2,0x7b,0xf1,0x6d,0xc7,0x8c,0x9d,0x86,0x3b,0x66,0xfa,0xb6,0x42,0x6f,0x14,0xd0,0x19,0xaf,0x11,0x21,0x96,0x85,0x91,0xb5,0xa0,0x1b,0x18,0xa6,0xa2,0x4b,0x40,0xd4,0x8d,0x2a,0x8b,0x5c,0x2c,0xe6,0xfe,0xa4,0x30,0xe7,0xff,0xc8,0x5f,0xe2,0x1c,0xdf,0xae,0x7f,0xc3,0x61,0xef,0x90,0x6c,0x51,0x2f,0xec,0x12,0x7a,0xaa,0xdd,0x77,0xf5,0x4,0xd9,0x83,0x33,0xeb,0x80,0x27,0x3,0xb4,0x9,0x37,0x6b,0x41,0x4f,0x7e,0xf3,0x24,0xf4,0xc9,0x7,0xd1,0x45,0x70,0xa1,0xd7,0x34,0x93,0x15,0xca,0x4c,0xcd,0x97,0xb9,0xea,0x0,0x5e,0x1a,0x9a,0xcf,0x79,0xa,0x3e,0x82,0xd3,0x68,0x75,0x64,0xce,0x55,0xe,0xbe,0x1d,0xe4,0xc1,0xc6,0xde,0xcc,0x1e,0x17,0xd6,0xdc,0x53,0x76};
        do
        {
          v3 = (v3 + 1) % 256;
          v5 = *(int *)(result + v3);
          v4 = (v5 + v4) % 256;
          *(int *)(result + v3) = *(int *)(result + v4);
          *(int *)(result + v4) = v5;
          a2[i] ^= *(int *)(result + ((*(int *)(result + v3) + v5) & 0xFF));
          --a3;
          ++i;
        }
        while ( a3 );
      string ss="V7D=^,M.E";
      string flag="";
      for(int i=0;i<30;i++)
      {
          int j=i%9;
          flag+=ss[j]^a2[i];
      }
      cout << flag;
      return 0;
    }
  • 相关阅读:
    pointnet++之classification/train.py
    pointnet++的pytorch实现
    xavier初始化的简单推导
    z+f数据解析
    ubuntu安装dia
    卷积,reLu,池化的意义
    Split
    .net程序调试一:快速定位异常
    Memcached (第一篇)
    System.Web.Caching.Cache类 缓存 各种缓存依赖
  • 原文地址:https://www.cnblogs.com/sweetbaby/p/10933268.html
Copyright © 2011-2022 走看看