首页
社区
课程
招聘
[原创][原创]2019KCTF总决赛 第七题:东北奇闻 - 脱机算法
发表于: 2019-12-19 14:12 5139

[原创][原创]2019KCTF总决赛 第七题:东北奇闻 - 脱机算法

2019-12-19 14:12
5139

大致流程,脱机算法搞出来,然后分析







1-题目改了androidx.appcompat.app.AppCmpatActivity; 在里面找到showAssist     native
2-SO解密了2部分内存数据块,存放起来
3-手动抠出算法,内存加密函数2个,func1和func2 ,函数1直接可看,函数2 肉眼解一下 ollvm就可以找出算法
4-第二个函数func2是char加密,实际是个映射表,跑一遍255个字节就出来了
5-最后的比较函数根据第三步的表倒推出最原始的输入数据的一组地址

/**********************************************找到对应的判断函数*********************************************************/
registernative   对应的函数地址是   0x19f81
ida修改下sp,可以看到函数了
之后就是 通过 jni调用GetText获取输入值,这里假设先输入  gabcdefghijklmn来测试
最终根据JNI函数来找到内存字符串
  


跟着字符串走到 0x9ce4处,这里是把输入的字符  每隔8个传入,开头是解密一块内存,但是后边的运算是要用到这4个数组,


比较运气好的是 是连续的  大概0xC00长度位一块
所以直接dump保存下来到文件,后面解析用到

import idaapi
start_address = 0xAEE7B010
data_length = 0xc00 * 4
data = idaapi.dbg_read_memory(start_address , data_length)
fp = open('/Users/beita/tmp/pediy/ctfq4/barr_dump_bin', 'wb')
fp.write(data)
fp.close() 

这部分代码用来第一次进行计算,  大致是前4个字节和猴子哥字节分别保存起来,然后用 字节对应到上面的4个数组元素来进行  加减异或运算  ,然后ROR4 再运算 ,大概是4组数据
处理完之后  ,把加密后的2个数据 ,v37和v35的值传入到  DF18进行第二个加密


第二个加密函数分析      代码 在附件 abc.c文件里

这部分代码在附件里    像是被ollvm的,,不过肉眼过一下流程看看还好  
import idaapi
start_address = 0xAEE7B010
data_length = 0xc00 * 4
data = idaapi.dbg_read_memory(start_address , data_length)
fp = open('/Users/beita/tmp/pediy/ctfq4/barr_dump_bin', 'wb')
fp.write(data)
fp.close() 

这部分代码用来第一次进行计算,  大致是前4个字节和猴子哥字节分别保存起来,然后用 字节对应到上面的4个数组元素来进行  加减异或运算  ,然后ROR4 再运算 ,大概是4组数据
处理完之后  ,把加密后的2个数据 ,v37和v35的值传入到  DF18进行第二个加密


第二个加密函数分析      代码 在附件 abc.c文件里

这部分代码在附件里    像是被ollvm的,,不过肉眼过一下流程看看还好  
824行     先保存参数到v70   params1=0x5d  
  =================================================此时的param1是v104  也就是0x5d =======================================================
314行     然后算出v111   v111  = params1 >> 6 = 1
         				v112 = params1 & 3 = 1

1024行      v118 = (params1 >> 4) & 3 ^ v116;  此处的116是上一个的112   此处v118=0
			v119 = (params1 >> 2) & 3 ^ v115;  此处的115是上一个的111   此处v119=2
				v29 = -1811835405;
484行     计算     v101 
		LODWORD(v101) = 2 * params & 0xAA;
		HIDWORD(v101) = ((unsigned int)params >> 1) & 0x55;  
		v101= 400000aa   64位的
781行   算出v20  =  0xae  然后0xae保存起来、
         v20 = HIDWORD(v101) | v101;
        根据v20 算出v103  v102      
       v103 = v20 & 0xCC;         	0x8c 
       v102 = 4 * v20 & 0xCC;		0x88

1191行  根据v103 v102算出  V104  v105
	v104 = v102 | (v103 >> 2);    0xab
	v105 = o * (o - 1) & 1;      0


967行  对一开始的一个传入的0x5d的那个参数值重新赋值
后面是几个保存传入参数的变量都进行赋值
  =================================================此时的param1是v104  也就是0xab =======================================================
314行     然后算出v111   v111  = params1 >> 6 = 2      
         				v112 = params1 & 3 = 3
         			   v116 = v112
         			   v117=v111 ^ v112

1024行      v118 = (params1 >> 4) & 3 ^ v116;  此处的116是上一个的112   此处v118=1
			v119 = (params1 >> 2) & 3 ^ v115;  此处的115是上一个的111   此处v119=2

1141行       算出   v124 
 			LODWORD(v124) = v117 << 6;
			HIDWORD(v124) = 16 * v119 & 0x30;

354行     算出v125
		v125 = v124 | HIDWORD(v124);

终于来到了 1207行  计算返回结果
  v53 = v125 | 4 * v116 & 0xFFFFFFC;
   v58 = v53 & v118;
  v59 = v53 ^ (unsigned __int8)v118;
  v60 = v59 | v58;
  然后反转 v60 也就是 0xd4反转0x4d
继续往下跟 最终结果是通过 strcmp来比较的
可以看到只要前半部分能对应上就可以通过 


然后  68dd8a0f7065609e3106fb2bb1059423e80fb1347318ffeb83b8a074a7e6c9cf  
找出这个字符串
因为第二个加密算法已经分析过, 传入是 uint8_t类型 ,输出也是uint_8t类型,所以,这里可以搞一个映射表

typedef struct {
    uint8_t orig;
    uint8_t enc;
} CHAR_TABLE;
void create_table() {
    char sss[2048];
    memset(sss, 0, 2048);

    char *key = "68dd8a0f7065609e3106fb2bb1059423e80fb1347318ffeb83b8a074a7e6c9cfd9\n";
    size_t right_key_len = strlen(key) / 2 + 1;
    uint8_t after_key_elem[right_key_len];         // 加密后的key数组
    uint8_t before_key_elem[right_key_len];                        // 输入之前的值
    memset(after_key_elem, 0, right_key_len);
    memset(before_key_elem, 0, right_key_len);
    char *p = key;
    for (int i = 0; i < right_key_len; i++) {
        char k_tmp[3];
        memset(k_tmp, 0, 3);
        memcpy(k_tmp, p, 2);
        after_key_elem[i] = strtol(k_tmp, NULL, 16);
        p += 2;
    }


    CHAR_TABLE enc_table[256];
    for (int i = 0; i < 256; i++) {
        enc_table[i].orig = i;
        enc_table[i].enc = encrypt2(i); 
    }

    for (int i = 0; i < right_key_len; i++) {
        uint8_t v1 = after_key_elem[i];
        // 用加密后的每个元素取在映射表里遍历查找
        for (int k = 0; k < 256; k++) {
            CHAR_TABLE c = enc_table[k];
            if (v1 == c.enc) {
                before_key_elem[i] = c.orig;
                break;
            }
        }
    }

    printf("gogogo\n");
    printf("\n%s\n", sss);
}
然后得到 对应的表


68dd8a0f7065609e3106fb2bb1059423e80fb1347318ffeb83b8a074a7e6c9cfd9 字符串对应原始表在这里查找

运行一下看看脱机效果

比如这里我输入的是    gabcdefghijklmn  在手机上看 第一部分加密函数的结果  
用两组数据

*******************************************手机上*****************************************************************
输入 后先截取前8字节 gabcdefg     ,看结果下图, 传入 df18的是  0x5d2680eb  >> 24  那么就是说 
gabcdefg被加密成 0x5d2680eb  然后传入高8位字节  也就是  0x5d   
结果是输入0x5d  输出0xd4

******************************************* 脱机代码在附件*********************************************************************
看到输入gabcdefg     测试,然后
加密0x5d  输出0xd4,       
加密0x26输出   0xac

最后加密的结果也是在这里计算结束后后和     strcmp的2个参数 就是用的这里的第二个加密函数的结果来进行比较的,说明脱机加密是对的
 
这里证是对脱机的传入的值进行比较,说明算法是对的
*********************************************************************************************************************

脱机算法 如下 ,具体内容在附件里, 代码可以运行,改一下文件路径就可以运行

 
824行     先保存参数到v70   params1=0x5d  
  =================================================此时的param1是v104  也就是0x5d =======================================================
314行     然后算出v111   v111  = params1 >> 6 = 1
         				v112 = params1 & 3 = 1

1024行      v118 = (params1 >> 4) & 3 ^ v116;  此处的116是上一个的112   此处v118=0
			v119 = (params1 >> 2) & 3 ^ v115;  此处的115是上一个的111   此处v119=2
				v29 = -1811835405;
484行     计算     v101 
		LODWORD(v101) = 2 * params & 0xAA;
		HIDWORD(v101) = ((unsigned int)params >> 1) & 0x55;  
		v101= 400000aa   64位的
781行   算出v20  =  0xae  然后0xae保存起来、
         v20 = HIDWORD(v101) | v101;
        根据v20 算出v103  v102      
       v103 = v20 & 0xCC;         	0x8c 
       v102 = 4 * v20 & 0xCC;		0x88

1191行  根据v103 v102算出  V104  v105
	v104 = v102 | (v103 >> 2);    0xab
	v105 = o * (o - 1) & 1;      0


967行  对一开始的一个传入的0x5d的那个参数值重新赋值
后面是几个保存传入参数的变量都进行赋值
  =================================================此时的param1是v104  也就是0xab =======================================================
314行     然后算出v111   v111  = params1 >> 6 = 2      
         				v112 = params1 & 3 = 3
         			   v116 = v112
         			   v117=v111 ^ v112

1024行      v118 = (params1 >> 4) & 3 ^ v116;  此处的116是上一个的112   此处v118=1
			v119 = (params1 >> 2) & 3 ^ v115;  此处的115是上一个的111   此处v119=2

1141行       算出   v124 
 			LODWORD(v124) = v117 << 6;
			HIDWORD(v124) = 16 * v119 & 0x30;

354行     算出v125
		v125 = v124 | HIDWORD(v124);

终于来到了 1207行  计算返回结果
  v53 = v125 | 4 * v116 & 0xFFFFFFC;
   v58 = v53 & v118;
  v59 = v53 ^ (unsigned __int8)v118;
  v60 = v59 | v58;
  然后反转 v60 也就是 0xd4反转0x4d
继续往下跟 最终结果是通过 strcmp来比较的
可以看到只要前半部分能对应上就可以通过 


然后  68dd8a0f7065609e3106fb2bb1059423e80fb1347318ffeb83b8a074a7e6c9cf  
找出这个字符串

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-12-26 19:39 被贝a塔编辑 ,原因: 图搞错了
上传的附件:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//