首页
社区
课程
招聘
[原创]Android赛区题解
2015-10-19 12:07 6373

[原创]Android赛区题解

2015-10-19 12:07
6373
1> Cobb的记忆
下载apk后解包反编译,反编译出的check.smali代码有4万多行,有3个函数:
access$_T11306:解析字符串,会在check函数里调用。
access$_T15566:也是解析字符串,会在check里被反射调用。
check:这个就是验证函数了,会调用上面两个access函数处理加密过的字符串,这些字符串应该是反射调用的函数名。
看了一圈,没有找到计算结果的代码片段,感觉4万多行代码没办法逆算法,于是尝试插入log代码打包,把关键信息打印出来。
搜索return,可以发现check函数里面只有一个return点,在12348行(修改过的代码,位置可能跟原来的代码位置有出入,下同)左右的
    move-result-object v4

    check-cast v4, Ljava/lang/Boolean;

    invoke-virtual {v4}, Ljava/lang/Boolean;->booleanValue()Z
    :try_end_68
    .catch Ljava/lang/reflect/InvocationTargetException; {:try_start_68 .. :try_end_68} :catch_c

    move-result v4

    :goto_0
    return v4

可以看到v4是一个boolean型,那么这个值应该就是比较的结果了。再搜索一下goto_0,在36184行左右看到代码:
    const-wide/32 v4, 0xf4240

    rem-long v4, v8, v4

    const-wide/32 v6, 0x1e74e

    add-long/2addr v4, v6

    cmp-long v4, v4, v10 # 比较v4和v10

    if-nez v4, :cond_3 # 不相等跳到 cond_3: const/4 v4, 0x0    # v4 = false

                                #                                  goto/16 :goto_0

    const/4 v4, 0x1   # v4 = true

    goto/16 :goto_0

那么这儿就是与正确key比较的地方了,在这里打上log(感谢蒸米@wooyun的代码:http://drops.wooyun.org/papers/6045)。如下:
    const-wide/32 v6, 0x1e74e

    add-long/2addr v4, v6

    invoke-static {v4, v5}, Ljava/math/BigInteger;->valueOf(J)Ljava/math/BigInteger;
    move-result-object v7

    invoke-static {v7}, Lk2015/mzheng/MZLog;->Log(Ljava/lang/Object;)V

    cmp-long v4, v4, v10

    invoke-static {v10, v11}, Ljava/math/BigInteger;->valueOf(J)Ljava/math/BigInteger;
    move-result-object v10

    invoke-static {v10}, Lk2015/mzheng/MZLog;->Log(Ljava/lang/Object;)V

    if-nez v4, :cond_3

    const/4 v4, 0x1

    goto/16 :goto_0

这儿需要注意,java内置的long型是不能作为Object型传给log函数的,因此需要先转换为BigInteger型。另外,smali指令中一个long型是用两个寄存器存储的,在传参的时候要指定两个寄存器,如上面的v4,v5、v10,v11,否则运行时会报错。打印出的寄存器值如下图:

对于不同的输入,v4总是固定的520676,那么这个就是正确的key了。
得到了正确的key,还是不知道算法,于是往上面搜索v10,在29043行左右找到计算v10的算法,同样插入log,如下:
    add-int/lit8 v7, v17, 0x4

    move-object/from16 v16, v5

    move-object v9, v14

    move-wide/from16 v10, v20

    invoke-static {v10, v11}, Ljava/math/BigInteger;->valueOf(J)Ljava/math/BigInteger;
    move-result-object v10    # v10转换成BigInteger型

    invoke-static {v10}, Lk2015/mzheng/MZLog;->Log(Ljava/lang/Object;)V    # 打印log

    invoke-virtual {v10}, Ljava/math/BigInteger;->longValue()J
    move-result-wide v10    # 把v10还原为long型

    move/from16 v17, v7

    move-object v7, v15

    goto/16 :goto_1

这样就可以打印出每轮计算后的值,如下:

通过每一轮计算的结果,可以发现一个规律:
输入1111,第一轮计算结果是1112。1112-1111 = 1;
第二轮是1117,1117-1112 = 5;
第三轮是1126,1126-1117 = 9;
第4轮是1139,1139-1126  13;
...
每一轮与上一轮的差值是一个等差数列,因此很容易有算法解出答案:
i = 997
n = 520676
while i >= 1:
    n = n - i
    i = i - 4
print n

解出结果为395926。
2> Satio的要求
这个题最后是解掉了,但是没来得及提交答案比赛就结束了,比较可惜。
java层的逻辑比较简单,程序启动后会输出一个logcat,是"wojiushidaan"的莫斯码,这个并没有什么用。程序注册的onClick函数调用一个java层的Ch.a函数,它首先检查包的MD5值,如果不对就直接return false。这个步骤是用来防止重打包的,修改smali代码中的比较值就可以绕过去了。然后程序会调用libwbox.so中的ch函数,key的算法实际是存放在这里的。
取出这个so,拖进ida,简单看一下就能找到反调试的地方。sub_1284这个函数会读取/proc/pid/status中的TracerPid,如果发现不为0说明被调试,就发送信号杀死自己。so的.init_array中有个函数会创建一个线程循环调用这个函数。另外,在sub_1DE0中,算法开始之前也会调用一次这个函数。还有就是在算法自解密(详见下文)的过程中,也会查询这个值,如果不为0就会乱写一段代码,造成执行失败。
由上述分析,其实所有的反调试手段都依赖于/proc/pid/status中的TracerPid,只要保证它为0,就不会触发任何的反调试。因此可以通过修改内核的方式,使得这个值始终为0。修改kernel代码目录中的fs/proc/array.c,task_state是处理/proc/pid/status文件的函数,将191行的tpid修改为0,那么无论有没有被调试,/proc/pid/status中的TracerPid都会始终为0,这样就可以绕过反调试了。
绕过反调试之后,就尝试解密算法。程序本身的代码有一部分是加密的,在执行的过程中会一点点解密,然后返回的时候再次加密回去。在合适的地方下断点,dump内存,就可以获取解密后的代码了。过程中的主要函数如下:
相对偏移0x1DE0:这个函数会初始化一个16字节的内存,内容为0x00, 0x01, 0x02, ..., 0x0f,然后将用户的输入复制到这个地方。输入的有效位数是16字节,超出16字节的部分不参与运算。如果不足16字节,那么会被初始化的内容填充到16字节。随后这个函数就解密0x24C8处的代码,然后调用解密后的函数。调用0x24C8的指令地址为0x2198,在这个位置下断点,就可以获取解密后的0x24C8函数。
0x24C8这个函数首先处理input,伪代码:
for (i = 0; i < 16; i++)
    input[i] = input[i] + i;

随后像0x1DE0一样,解密下一个函数0x14A4。在0x2984处下断点,可以dump出解密后的0x14A4。
0x14A4这个函数同样首先处理input,这里用到了一个固定值的数组,地址为0x1DE0,内容如下:
(gdb) x/16xb $r0
x/16xb $r0
0x63e1fde0:     0x1f    0xbc    0xda    0xff    0xe6    0x4c    0xbc    0x44
0x63e1fde8:     0xf5    0xb8    0x13    0xc8    0xec    0xa8    0xcd    0xbd

伪代码:
for (i = 0; i < 16; i++)
    input[i] = input[i] + array[i];

然后,函数mmap一块内存,赋予执行权限,将算法代码复制进去解密并调用。算法的输入是上面处理过的input。然后与加密后的key值比较,将比较结果层层返回。加密后的key值是写死在代码里的,如下:
x/20xw 0x63E23D10
0x63e23d10:     0x2f77da5c      0x393ec6a3      0xedf3f0b6      0x86995a51

真正的算法实际是在这个mmap的内存中,在0x1944处下断点,可以dump出解密后的算法代码。
这个算法才是真正的开始,前面的反打包、反调试、代码的自解密跟算法一比根本不值一提。
IDA中F5后的代码如下:
int __fastcall sub_64da8000(char *a1, char *a2) { 
  out = a2;
  in = (int *)a1;
  v2 = 0x6BCDC67A;
  v3 = &v79;
  v4 = 0;
  v5 = (unsigned int)&v79 | 4;
  v80 = 0x6BCDC67A;
  *(_DWORD *)(v5 + 4) = 0x6B2B7C9D;
  *(_DWORD *)(v5 + 8) = 0x8DA459B1;
  v6 = 0xAB9D0680;
  v83 = 0xAB9D0680;
  while ( 1 )
  {
    if ( v4 & 3 )
    {
      v7 = (int)&v3[v4];
      v6 ^= v2;
    }
    else
    {
      v8 = v3;
      v9 = *(_WORD *)(int_64DA8758() + (((unsigned int)v6 >> 15) & 0x1FE));
      v10 = (*(_WORD *)(int_64DA8758() + (((unsigned int)v6 >> 7) & 0x1FE)) << 16) & 0xFF0000 | (v9 << 24);
      v11 = v10 | (*(_WORD *)(int_64DA8758() + 2 * (unsigned __int8)v6) << 8) & 0xFF00;
      v12 = (v11 | *(_BYTE *)(int_64DA8758() + (((unsigned int)v6 >> 23) & 0x1FE))) ^ v2;
      v13 = sub_64DA8958();
      v3 = v8;
      v6 = v12 ^ *(_DWORD *)(v13 + ((v4 + ((unsigned int)(v4 >> 31) >> 30)) & 0xFFFFFFFC));
      v7 = (int)&v8[v4];
    }
    *(_DWORD *)(v7 + 20) = v6;
    if ( v4 == 39 )
      break;
    v2 = v3[v4++ + 2];
  }
  v79 = 10;
  xor_4 = _byteswap_ulong(in[3]) ^ v83;         // 0x80741682
  xor_3 = _byteswap_ulong(in[2]) ^ v82;         // 0xbb5517b4
  xor_2 = _byteswap_ulong(in[1]) ^ v81;         // 0x74ac851e
  v16 = v3 + 8;                                 // 
                                                // 
  xor_1 = _byteswap_ulong(*in) ^ v80;           // 0x3b22c94c
  v18 = 0;
  do
  {
    ina = v16;                                  // 0x64d88e58
    v19 = xor_1;
    out_4 = xor_3;
    out_8 = xor_2;
    out_12 = v18;
    v23 = (xor_1 >> 23) & 0x1FE;
    v24 = int_64DA8758();
    v25 = xor_2;
    v26 = ((*(_WORD *)(v24 + v23) & 0xFF) << 8) | (*(_WORD *)(v24 + v23) << 16) | *(_WORD *)(v24 + v23) & 0xFF ^ ((unsigned int)*(_WORD *)(v24 + v23) >> 8);
    v27 = int_64DA8758() + ((xor_2 >> 15) & 0x1FE);
    v28 = (*(_WORD *)v27 & 0xFF | (*(_WORD *)v27 << 8) | ((*(_WORD *)v27 ^ ((unsigned int)*(_WORD *)v27 >> 8)) << 24)) ^ v26;
    v29 = *(_WORD *)(int_64DA8758() + ((xor_3 >> 7) & 0x1FE));
    v30 = *(_WORD *)(int_64DA8758() + 2 * (unsigned __int8)xor_4);
    at = v28 ^ *(ina - 3) ^ (v29 | (((unsigned __int8)v29 ^ (v29 >> 8) | (v29 << 8)) << 16)) ^ (((((unsigned __int8)v30 << 8) | (v30 << 16) | (unsigned __int8)v30 ^ (v30 >> 8)) << 8) | (v30 >> 8));
    v31 = int_64DA8758();
    v32 = ((*(_WORD *)(v31 + ((v25 >> 23) & 0x1FE)) & 0xFF) << 8) | (*(_WORD *)(v31 + ((v25 >> 23) & 0x1FE)) << 16) | *(_WORD *)(v31 + ((v25 >> 23) & 0x1FE)) & 0xFF ^ ((unsigned int)*(_WORD *)(v31 + ((v25 >> 23) & 0x1FE)) >> 8);
    v33 = *(_WORD *)(int_64DA8758() + ((xor_3 >> 15) & 0x1FE));
    v34 = ((unsigned __int8)v33 | (v33 << 8) | ((v33 ^ (v33 >> 8)) << 24)) ^ v32;
    v35 = *(_WORD *)(int_64DA8758() + (((unsigned int)xor_4 >> 7) & 0x1FE));
    v36 = *(_WORD *)(int_64DA8758() + 2 * (unsigned __int8)v19);
    bt = v34 ^ *(ina - 2) ^ (v35 | (((unsigned __int8)v35 ^ (v35 >> 8) | (v35 << 8)) << 16)) ^ (((((unsigned __int8)v36 << 8) | (v36 << 16) | (unsigned __int8)v36 ^ (v36 >> 8)) << 8) | (v36 >> 8));
    v37 = *(_WORD *)(int_64DA8758() + ((xor_3 >> 23) & 0x1FE));
    v38 = ((unsigned __int8)v37 << 8) | (v37 << 16) | (unsigned __int8)v37 ^ (v37 >> 8);
    v39 = *(_WORD *)(int_64DA8758() + (((unsigned int)xor_4 >> 15) & 0x1FE));
    v40 = ((unsigned __int8)v39 | (v39 << 8) | ((v39 ^ (v39 >> 8)) << 24)) ^ v38;
    v41 = *(_WORD *)(int_64DA8758() + ((v19 >> 7) & 0x1FE));
    v42 = *(_WORD *)(int_64DA8758() + 2 * (unsigned __int8)out_8);
    ct = v40 ^ *(ina - 1) ^ (v41 | (((unsigned __int8)v41 ^ (v41 >> 8) | (v41 << 8)) << 16)) ^ (((((unsigned __int8)v42 << 8) | (v42 << 16) | (unsigned __int8)v42 ^ (v42 >> 8)) << 8) | (v42 >> 8));
    v44 = *(_WORD *)(int_64DA8758() + (((unsigned int)xor_4 >> 23) & 0x1FE));
    v45 = ((unsigned __int8)v44 << 8) | (v44 << 16) | (unsigned __int8)v44 ^ (v44 >> 8);
    v46 = int_64DA8758();
    v47 = (*(_WORD *)(v46 + ((v19 >> 15) & 0x1FE)) & 0xFF | (*(_WORD *)(v46 + ((v19 >> 15) & 0x1FE)) << 8) | ((*(_WORD *)(v46 + ((v19 >> 15) & 0x1FE)) ^ ((unsigned int)*(_WORD *)(v46 + ((v19 >> 15) & 0x1FE)) >> 8)) << 24)) ^ v45;
    v48 = *(_WORD *)(int_64DA8758() + ((out_8 >> 7) & 0x1FE));
    v49 = int_64DA8758();
    v16 = ina + 4;
    v50 = v48 | (((unsigned __int8)v48 ^ (v48 >> 8) | (v48 << 8)) << 16);
    xor_3 = ct;
    v51 = *(_WORD *)(v49 + 2 * out_4);
    xor_4 = v47 ^ *ina ^ v50 ^ (((((unsigned __int8)v51 << 8) | (v51 << 16) | (unsigned __int8)v51 ^ (v51 >> 8)) << 8) | (v51 >> 8));
    xor_2 = bt;
    v18 = out_12 + 1;
    xor_1 = at;
  }
  while ( out_12 + 1 < v79 - 1 );
  v52 = ina[1];
  v53 = *(_WORD *)(int_64DA8758() + (((unsigned int)at >> 23) & 0x1FE));
  v54 = (*(_WORD *)(int_64DA8758() + (((unsigned int)bt >> 15) & 0x1FE)) << 16) & 0xFF0000 | (v53 << 24);
  v55 = v54 | (*(_WORD *)(int_64DA8758() + (((unsigned int)ct >> 7) & 0x1FE)) << 8) & 0xFF00;
  v56 = (v55 | *(_BYTE *)(int_64DA8758() + 2 * (unsigned __int8)xor_4)) ^ v52;
  *out = BYTE3(v56);
  out[1] = (unsigned int)v56 >> 16;
  out[2] = BYTE1(v56);
  out[3] = v56;
  v57 = ina[2];
  v58 = *(_WORD *)(int_64DA8758() + (((unsigned int)bt >> 23) & 0x1FE));
  v59 = (*(_WORD *)(int_64DA8758() + (((unsigned int)ct >> 15) & 0x1FE)) << 16) & 0xFF0000 | (v58 << 24);
  v60 = v59 | (*(_WORD *)(int_64DA8758() + (((unsigned int)xor_4 >> 7) & 0x1FE)) << 8) & 0xFF00;
  v61 = (v60 | *(_BYTE *)(int_64DA8758() + 2 * (unsigned __int8)at)) ^ v57;
  out[4] = BYTE3(v61);
  out[5] = (unsigned int)v61 >> 16;
  out[6] = BYTE1(v61);
  out[7] = v61;
  v62 = ina[3];
  v63 = *(_WORD *)(int_64DA8758() + (((unsigned int)ct >> 23) & 0x1FE));
  v64 = (*(_WORD *)(int_64DA8758() + (((unsigned int)xor_4 >> 15) & 0x1FE)) << 16) & 0xFF0000 | (v63 << 24);
  v65 = v64 | (*(_WORD *)(int_64DA8758() + (((unsigned int)at >> 7) & 0x1FE)) << 8) & 0xFF00;
  v66 = (v65 | *(_BYTE *)(int_64DA8758() + 2 * (unsigned __int8)bt)) ^ v62;
  out[8] = BYTE3(v66);
  out[9] = (unsigned int)v66 >> 16;
  out[10] = BYTE1(v66);
  out[11] = v66;
  v67 = *v16;
  v68 = *(_WORD *)(int_64DA8758() + (((unsigned int)xor_4 >> 23) & 0x1FE));
  v69 = (*(_WORD *)(int_64DA8758() + (((unsigned int)at >> 15) & 0x1FE)) << 16) & 0xFF0000 | (v68 << 24);
  v70 = v69 | (*(_WORD *)(int_64DA8758() + (((unsigned int)bt >> 7) & 0x1FE)) << 8) & 0xFF00;
  v71 = (v70 | *(_BYTE *)(int_64DA8758() + 2 * (unsigned __int8)ct)) ^ v67;
  out[12] = BYTE3(v71);
  out[13] = (unsigned int)v71 >> 16;
  out[14] = BYTE1(v71);
  out[15] = v71;
  return 0;
}

代码大致可以分为两个循环,第一个循环用来初始化一个int型数组,数组为定值。在第二个循环中这个数组会参与运算。随后将输入的16字节按大字端产生4个int值,暂且称之为a,b,c,d。
这4个值会首先与4个定值进行异或,然后进入第二个循环,共9轮。
整个算法如下(略去产生数组的第一层循环,可以认为是一个常量):
约定b1(a)为取a数学意义上的最高两位,如b1(0x12345678) = 0x12。b2,b3,b4依次类推。
约定":"为数字的连接,如0x12:0x34:0x56:0x78 = 0x12345678。
约定h(a)为取一个short型数字的高8位,如h(0x1234) = 0x12;l(a)为取一个short型数字的低8位,如l(0x1234) = 0x34。
其中每轮有4种算法,称之为m1,m2,m3,m4。其中:
m1(t) = h(t) : l(t) : l(t) : (h(t)^l(t))
m2(t) = (h(t)^l(t)) : h(t) : l(t) : l(t)
m3(t) = l(t) : (h(t)^l(t)) : h(t) : l(t)
m4(t) = l(t) : l(t) : (h(t)^l(t)) : h(t)
算法存在一个定值数组,为256个short型,共512字节,记为M。
同时存在第一个循环初始化过的定值数组,为40个int型,记为X。

/* 大字端扩展 */
a = in[0] : in[1] : in[2] : in[3];
b = in[4] : in[5] : in[6] : in[7];
c = in[8] : in[9] : in[10] : in[11];
d = in[12] : in[13] : in[14] : in[15];

a = a ^ 0x6bcdc67a;
b = b ^ 0x6b2b7c9d;
c = c ^ 0x8da459b1;
d = d ^ 0xab9d0680;

i = 0;
do {
    at = m1(M[b1(a)]) ^ m2(M[b2(b)]) ^ m3(M[b3(c)]) ^ m4(M[b4(d)]) ^ X[i];
    bt = m1(M[b1(b)]) ^ m2(M[b2(c)]) ^ m3(M[b3(d)]) ^ m4(M[b4(a)]) ^ X[i+1];
    ct = m1(M[b1(c)]) ^ m2(M[b2(d)]) ^ m3(M[b3(a)]) ^ m4(M[b4(b)]) ^ X[i+2];
    dt = m1(M[b1(d)]) ^ m2(M[b2(a)]) ^ m3(M[b3(b)]) ^ m4(M[b4(c)]) ^ X[i+3];
    a = at;
    b = bt;
    c = ct;
    d = dt;
    i++;
} while (i < 9);

/* 最后的处理 */
af = l(M[b1(a)]) : l(M[b2(b)]) : l(M[b3(c)]) : l(M[b4(d)]);
af = af ^ X[36];
bf = l(M[b1(b)]) : l(M[b2(c)]) : l(M[b3(d)]) : l(M[b4(a)]);
bf = bf ^ X[37];
cf = l(M[b1(c)]) : l(M[b2(d)]) : l(M[b3(a)]) : l(M[b4(b)]);
cf = cf ^ X[38];
df = l(M[b1(d)]) : l(M[b2(a)]) : l(M[b3(b)]) : l(M[b4(d)]);
df = df ^ X[39];

/* 保存到out,同样是大字端 */
out[0] = b1(af);
out[1] = b2(af);
out[2] = b3(af);
out[3] = b4(af);

out[4] = b1(bf);
out[5] = b2(bf);
out[6] = b3(bf);
out[7] = b4(bf);

out[8] = b1(cf);
out[9] = b2(cf);
out[10] = b3(cf);
out[11] = b4(cf);

out[12] = b1(df);
out[13] = b2(df);
out[14] = b3(df);
out[15] = b4(df);

逆出算法之后,觉得眼一黑:这个算法(我觉得)是无法逆运算的!只能采用暴力破解的方式。
仔细看“最后的处理”部分的代码,从af,bf,cf,df逆运算得到最后一轮的a,b,c,d比较简单,因为af的每个字节只与上轮的a,b,c,d中的一个值有关,有
b1(af ^ X[36]) = l(M[b1(a)])。这样只需要一层循环,就可以获得最后一轮a中的某一位。
for i in range(len(M)):
    if l(M[i]) = b1(af ^ x[36]):
        #此时的i就是上轮a的第一个字节,b1(a)

依次类推,我们可以获取最后一轮的a、b、c、d。
对于9轮循环,问题就比较麻烦,因为a中一个字节与上轮循环中的a、b、c、d都有关系,虽然说有了正向的算法后可以通过4层循环来跑,但是4层循环太慢了。当时我测试了一下,在我机器上跑一个字节就要好几分钟,一轮跑4个字节,9轮,速度太慢了,感觉不应该这么粗暴的解。
仔细观察每轮循环中的算法,可以发现一个规律,这里以a为例说明。
at = m1(M[b1(a)]) ^ m2(M[b2(b)]) ^ m3(M[b3(c)]) ^ m4(M[b4(d)]) ^ X[i];
首先跟定值X[i]异或,结果以ax代替,即
ax = m1(M[b1(a)]) ^ m2(M[b2(b)]) ^ m3(M[b3(c)]) ^ m4(M[b4(d)]);
然后展开其中的m,对于h(M[b1(a)]),用ha代替,有
ax = (ha:la:la:ha^la) ^ (hb^lb:hb:lb:lb) ^ (lc:hc^lc:hc:lc) ^ (lc:lc:hc^lc:hc)
对于ax的每一个字节,记为a1, a2, a3, a4。于是,
a1 = b1(ax) = ha ^ (hb ^ lb) ^ lc ^ ld
a2 = b2(ax) = la ^ hb ^ (hc ^ lc) ^ ld
a3 = b3(ax) = la ^ lb ^ hc ^ (hd ^ ld)
a4 = b4(ax) = (ha ^ la) ^ lb ^ lc ^ hd
对于异或运算,它满足交换律和结合律,即
x ^ y = y ^ x
(x ^ y) ^ z = x ^ (y ^ z)
因此对于x1 ^ x2 ^ x3 ^ ... ^ xn,其顺序可以任意改变而不会应该其结果。
另外,异或还有这样的性质:
x ^ x = 0
x ^ 0 = x
因此,有下面的等式,
t1
= a1 ^ a2 ^ a4 
= (ha ^ (hb ^ lb) ^ lc ^ ld) ^ (la ^ hb ^ (hc ^ lc) ^ ld) ^ ((ha ^ la) ^ lb ^ lc ^ hd)
= (ha ^ la ^ ha ^ la) ^ (hb ^ lb ^ hb ^ lb) ^ (lc ^ hc ^ lc ^ lc) ^ (ld ^ ld ^ hd)
= hc ^ lc ^ hd
同理,有
t2 = a1 ^ a3 ^ a4 = hb ^ lb ^ hc
t3 = a2 ^ a3 ^ a4 = ha ^ la ^ hb
t4 = a1 ^ a2 ^ a3 = ha ^ hd ^ ld
这样,就有了4个约束条件。
于是代码就可以这样写:
for i in range(len(M)):
    for j in range(len(M)):
        if h(M[i]) ^ l(M[i]) ^ h(M[j]) == t3:
            for k in range(len(M)):
                if h(M[j]) ^ l(M[j]) ^ h(M[k]) == t2:
                    for q in range(len(M)):
                        if h(M[k]) ^ l(M[k]) ^ h(M[q]) == t1:
                            if h(M[i]) ^ h(M[q]) ^ l(M[q]) == t4:
                                # 上轮中a、b、c、d中的某一位
                                at1 = i
                                bt2 = j
                                ct3 = k
                                dt4 = q
这样在第二、三层循环中就加入了约束条件,大大减少循环次数。在我机器上,9轮循环,一轮4个字节,4.5秒左右就可以跑完。
依次类推到本轮的b、c、d,就可以获得其他字节,组合就得到了上轮的a、b、c、d。

循环问题解决后,其他的算法就是加加减减和异或,不再赘述。
最后放上我的解密代码,求得结果为kboloy0
import time

matrix = [
0xC663 ,
0xF87C ,
0xEE77 ,
0xF67B ,
0xFFF2 ,
0xD66B ,
0xDE6F ,
0x91C5 ,
0x6030 ,
0x201  ,
0xCE67 ,
0x562B ,
0xE7FE ,
0xB5D7 ,
0x4DAB ,
0xEC76 ,
0x8FCA ,
0x1F82 ,
0x89C9 ,
0xFA7D ,
0xEFFA ,
0xB259 ,
0x8E47 ,
0xFBF0 ,
0x41AD ,
0xB3D4 ,
0x5FA2 ,
0x45AF ,
0x239C ,
0x53A4 ,
0xE472 ,
0x9BC0 ,
0x75B7 ,
0xE1FD ,
0x3D93 ,
0x4C26 ,
0x6C36 ,
0x7E3F ,
0xF5F7 ,
0x83CC ,
0x6834 ,
0x51A5 ,
0xD1E5 ,
0xF9F1 ,
0xE271 ,
0xABD8 ,
0x6231 ,
0x2A15 ,
0x804  ,
0x95C7 ,
0x4623 ,
0x9DC3 ,
0x3018 ,
0x3796 ,
0xA05  ,
0x2F9A ,
0xE07  ,
0x2412 ,
0x1B80 ,
0xDFE2 ,
0xCDEB ,
0x4E27 ,
0x7FB2 ,
0xEA75 ,
0x1209 ,
0x1D83 ,
0x582C ,
0x341A ,
0x361B ,
0xDC6E ,
0xB45A ,
0x5BA0 ,
0xA452 ,
0x763B ,
0xB7D6 ,
0x7DB3 ,
0x5229 ,
0xDDE3 ,
0x5E2F ,
0x1384 ,
0xA653 ,
0xB9D1 ,
0      ,
0xC1ED ,
0x4020 ,
0xE3FC ,
0x79B1 ,
0xB65B ,
0xD46A ,
0x8DCB ,
0x67BE ,
0x7239 ,
0x944A ,
0x984C ,
0xB058 ,
0x85CF ,
0xBBD0 ,
0xC5EF ,
0x4FAA ,
0xEDFB ,
0x8643 ,
0x9A4D ,
0x6633 ,
0x1185 ,
0x8A45 ,
0xE9F9 ,
0x402  ,
0xFE7F ,
0xA050 ,
0x783C ,
0x259F ,
0x4BA8 ,
0xA251 ,
0x5DA3 ,
0x8040 ,
0x58F  ,
0x3F92 ,
0x219D ,
0x7038 ,
0xF1F5 ,
0x63BC ,
0x77B6 ,
0xAFDA ,
0x4221 ,
0x2010 ,
0xE5FF ,
0xFDF3 ,
0xBFD2 ,
0x81CD ,
0x180C ,
0x2613 ,
0xC3EC ,
0xBE5F ,
0x3597 ,
0x8844 ,
0x2E17 ,
0x93C4 ,
0x55A7 ,
0xFC7E ,
0x7A3D ,
0xC864 ,
0xBA5D ,
0x3219 ,
0xE673 ,
0xC060 ,
0x1981 ,
0x9E4F ,
0xA3DC ,
0x4422 ,
0x542A ,
0x3B90 ,
0xB88  ,
0x8C46 ,
0xC7EE ,
0x6BB8 ,
0x2814 ,
0xA7DE ,
0xBC5E ,
0x160B ,
0xADDB ,
0xDBE0 ,
0x6432 ,
0x743A ,
0x140A ,
0x9249 ,
0xC06  ,
0x4824 ,
0xB85C ,
0x9FC2 ,
0xBDD3 ,
0x43AC ,
0xC462 ,
0x3991 ,
0x3195 ,
0xD3E4 ,
0xF279 ,
0xD5E7 ,
0x8BC8 ,
0x6E37 ,
0xDA6D ,
0x18D  ,
0xB1D5 ,
0x9C4E ,
0x49A9 ,
0xD86C ,
0xAC56 ,
0xF3F4 ,
0xCFEA ,
0xCA65 ,
0xF47A ,
0x47AE ,
0x1008 ,
0x6FBA ,
0xF078 ,
0x4A25 ,
0x5C2E ,
0x381C ,
0x57A6 ,
0x73B4 ,
0x97C6 ,
0xCBE8 ,
0xA1DD ,
0xE874 ,
0x3E1F ,
0x964B ,
0x61BD ,
0xD8B  ,
0xF8A  ,
0xE070 ,
0x7C3E ,
0x71B5 ,
0xCC66 ,
0x9048 ,
0x603  ,
0xF7F6 ,
0x1C0E ,
0xC261 ,
0x6A35 ,
0xAE57 ,
0x69B9 ,
0x1786 ,
0x99C1 ,
0x3A1D ,
0x279E ,
0xD9E1 ,
0xEBF8 ,
0x2B98 ,
0x2211 ,
0xD269 ,
0xA9D9 ,
0x78E  ,
0x3394 ,
0x2D9B ,
0x3C1E ,
0x1587 ,
0xC9E9 ,
0x87CE ,
0xAA55 ,
0x5028 ,
0xA5DF ,
0x38C  ,
0x59A1 ,
0x989  ,
0x1A0D ,
0x65BF ,
0xD7E6 ,
0x8442 ,
0xD068 ,
0x8241 ,
0x2999 ,
0x5A2D ,
0x1E0F ,
0x7BB0 ,
0xA854 ,
0x6DBB ,
0x2C16 ,
]

xor_m = [
0x34a20b18, 0x5f897785, 0xd22d2e34, 0x79b028b4 ,
0xd19686ae, 0x8e1ff12b, 0x5c32df1f, 0x2582f7ab ,
0xc6fee491, 0x48e115ba, 0x14d3caa5, 0x31513d0e ,
0x1fd94f56, 0x57385aec, 0x43eb9049, 0x72baad47 ,
0xfb4cef16, 0xac74b5fa, 0xef9f25b3, 0x9d2588f4 ,
0xe4885048, 0x48fce5b2, 0xa763c001, 0x3a4648f5 ,
0xfedab6c8, 0xb626537a, 0x1145937b, 0x2b03db8e ,
0x0563af39, 0xb345fc43, 0xa2006f38, 0x8903b4b6 ,
0x65eee19e, 0xd6ab1ddd, 0x74ab72e5, 0xfda8c653 ,
0x915a0cca, 0x47f11117, 0x335a63f2, 0xcef2a5a1 ,
]

def b1(n):
    return (n >> 24) & 0xff;

def b2(n):
    return (n >> 16) & 0xff;

def b3(n):
    return (n >> 8) & 0xff;

def b4(n):
    return n & 0xff;

def m1(t):
    th = (t >> 8) & 0xff
    tl = t & 0xff
    return th << 24 | tl << 16 | tl << 8 | (th ^ tl)

def m2(t):
    th = (t >> 8) & 0xff
    tl = t & 0xff
    return (th ^ tl) << 24 | th << 16 | tl << 8 | tl

def m3(t):
    th = (t >> 8) & 0xff
    tl = t & 0xff
    return tl << 24 | (th ^ tl) << 16 | th << 8 | tl

def m4(t):
    th = (t >> 8) & 0xff
    tl = t & 0xff
    return tl << 24 | tl << 16 | (tl ^ th) << 8 | th

def geth(t):
    return (t >> 8) & 0xff

def getl(t):
    return t & 0xff

def find_l(l):
    for i in range(len(matrix)):
        if matrix[i] & 0xff == l:
            print hex(i)
            return i

def find_h(h):
    for i in range(len(matrix)):
        if (matrix[i] & 0xff00) >> 8 == h:
            print hex(i)
            return i

def merge_int(a1,a2,a3,a4):
    return a1 << 24 | a2 << 16 | a3 << 8 | a4

# 加密后的正确key
af = 0x2f77da5c
bf = 0x393ec6a3
cf = 0xedf3f0b6
df = 0x86995a51

'''
# 输入1234567890123456的计算结果
af = 0x128b387f
bf = 0x489b84af
cf = 0x4940ab58
df = 0x858b3ae5
'''

af = b4(af) << 24 | b3(af) << 16 | b2(af) << 8 | b1(af)
bf = b4(bf) << 24 | b3(bf) << 16 | b2(bf) << 8 | b1(bf)
cf = b4(cf) << 24 | b3(cf) << 16 | b2(cf) << 8 | b1(cf)
df = b4(df) << 24 | b3(df) << 16 | b2(df) << 8 | b1(df)
print hex(af),hex(bf),hex(cf),hex(df)

af = af ^ xor_m[36]
bf = bf ^ xor_m[37]
cf = cf ^ xor_m[38]
df = df ^ xor_m[39]


at1 = find_l(b1(af))
bt1 = find_l(b1(bf))
ct1 = find_l(b1(cf))
dt1 = find_l(b1(df))

bt2 = find_l(b2(af))
ct2 = find_l(b2(bf))
dt2 = find_l(b2(cf))
at2 = find_l(b2(df))

ct3 = find_l(b3(af))
dt3 = find_l(b3(bf))
at3 = find_l(b3(cf))
bt3 = find_l(b3(df))

dt4 = find_l(b4(af))
at4 = find_l(b4(bf))
bt4 = find_l(b4(cf))
ct4 = find_l(b4(df))

i = 8

start = time.time()
while i >= -1:
    print "====================================="
    am = merge_int(at1, at2, at3, at4)
    bm = merge_int(bt1, bt2, bt3, bt4)
    cm = merge_int(ct1, ct2, ct3, ct4)
    dm = merge_int(dt1, dt2, dt3, dt4)
    print hex(am),hex(bm),hex(cm),hex(dm)
    if (i == -1):
        print "done."
        break
    

    am = am ^ xor_m[i*4]
    bm = bm ^ xor_m[i*4 +1]
    cm = cm ^ xor_m[i*4 + 2]
    dm = dm ^ xor_m[i*4 + 3]
    print hex(am),hex(bm),hex(cm),hex(dm)

    # hd ^ hc ^ lc
    t1 = b1(am) ^ b2(am) ^ b4(am)
    # hb ^ lb ^ hc
    t2 = b1(am) ^ b3(am) ^ b4(am)
    # ha ^ la ^ hb
    t3 = b2(am) ^ b3(am) ^ b4(am)
    # ha ^ hd ^ ld
    t4 = b1(am) ^ b2(am) ^ b3(am)

    for j in range(256):
        for k in range(256):
            if geth(matrix[j]) ^ getl(matrix[j]) ^ geth(matrix[k]) == t3:
                for l in range(256):
                    if geth(matrix[k]) ^ getl(matrix[k]) ^ geth(matrix[l]) == t2:
                        for m in range(256):
                            if geth(matrix[m]) ^ geth(matrix[l]) ^ getl(matrix[l]) == t1:
                                if geth(matrix[j]) ^ geth(matrix[m]) ^ getl(matrix[m]) == t4:
                                    at1 = j
                                    bt2 = k
                                    ct3 = l
                                    dt4 = m
                                    break


    # a d d
    t1 = b1(bm) ^ b2(bm) ^ b4(bm)
    # c c d
    t2 = b1(bm) ^ b3(bm) ^ b4(bm)
    # b b c
    t3 = b2(bm) ^ b3(bm) ^ b4(bm)
    # b a a
    t4 = b1(bm) ^ b2(bm) ^ b3(bm)

    for j in range(256):
        for k in range(256):
            if geth(matrix[j]) ^ getl(matrix[j]) ^ geth(matrix[k]) == t3:
                for l in range(256):
                    if geth(matrix[k]) ^ getl(matrix[k]) ^ geth(matrix[l]) == t2:
                        for m in range(256):
                            if geth(matrix[m]) ^ geth(matrix[l]) ^ getl(matrix[l]) == t1:
                                if geth(matrix[j]) ^ geth(matrix[m]) ^ getl(matrix[m]) == t4:
                                    bt1 = j
                                    ct2 = k
                                    dt3 = l
                                    at4 = m
                                    break


    # b a a
    t1 = b1(cm) ^ b2(cm) ^ b4(cm)
    # d d a
    t2 = b1(cm) ^ b3(cm) ^ b4(cm)
    # c c d
    t3 = b2(cm) ^ b3(cm) ^ b4(cm)
    # c b b
    t4 = b1(cm) ^ b2(cm) ^ b3(cm)

    for j in range(256):
        for k in range(256):
            if geth(matrix[j]) ^ getl(matrix[j]) ^ geth(matrix[k]) == t3:
                for l in range(256):
                    if geth(matrix[k]) ^ getl(matrix[k]) ^ geth(matrix[l]) == t2:
                        for m in range(256):
                            if geth(matrix[m]) ^ geth(matrix[l]) ^ getl(matrix[l]) == t1:
                                if geth(matrix[j]) ^ geth(matrix[m]) ^ getl(matrix[m]) == t4:
                                    ct1 = j
                                    dt2 = k
                                    at3 = l
                                    bt4 = m
                                    break


    # c b b
    t1 = b1(dm) ^ b2(dm) ^ b4(dm)
    # a a b
    t2 = b1(dm) ^ b3(dm) ^ b4(dm)
    # d d a
    t3 = b2(dm) ^ b3(dm) ^ b4(dm)
    # d c c
    t4 = b1(dm) ^ b2(dm) ^ b3(dm)

    for j in range(256):
        for k in range(256):
            if geth(matrix[j]) ^ getl(matrix[j]) ^ geth(matrix[k]) == t3:
                for l in range(256):
                    if geth(matrix[k]) ^ getl(matrix[k]) ^ geth(matrix[l]) == t2:
                        for m in range(256):
                            if geth(matrix[m]) ^ geth(matrix[l]) ^ getl(matrix[l]) == t1:
                                if geth(matrix[j]) ^ geth(matrix[m]) ^ getl(matrix[m]) == t4:
                                    dt1 = j
                                    at2 = k
                                    bt3 = l
                                    ct4 = m
                                    break
    i = i - 1

print "time: " + str(time.time() - start)

am = am ^ 0x6bcdc67a
bm = bm ^ 0x6b2b7c9d
cm = cm ^ 0x8da459b1
dm = dm ^ 0xab9d0680

print hex(am),hex(bm),hex(cm),hex(dm)

sub_a1 = b1(am)
sub_a2 = b2(am)
sub_a3 = b3(am)
sub_a4 = b4(am)

sub_b1 = b1(bm)
sub_b2 = b2(bm)
sub_b3 = b3(bm)
sub_b4 = b4(bm)

sub_c1 = b1(cm)
sub_c2 = b2(cm)
sub_c3 = b3(cm)
sub_c4 = b4(cm)

sub_d1 = b1(dm)
sub_d2 = b2(dm)
sub_d3 = b3(dm)
sub_d4 = b4(dm)

subee = [0x1f,0xbc,0xda,0xff,0xe6,0x4c,0xbc,0x44,0xf5,0xb8,0x13,0xc8,0xec,0xa8,0xcd,0xbd]
sub_list = [sub_a1, sub_a2, sub_a3, sub_a4, sub_b1, sub_b2, sub_b3, sub_b4, sub_c1, sub_c2, sub_c3, sub_c4, sub_d1, sub_d2, sub_d3, sub_d4]

sub_re = []

for i in range(len(sub_list)):
    print hex(sub_list[i]),
    sub_re.append((sub_list[i] + 0x100 - subee[i]) & 0xff)

print ""
passwd = []
for i in range(len(sub_re)):
    passwd.append(chr(sub_re[i] - i))

print "passwd: " + str(passwd)

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小寿 2016-2-18 10:16
2
0
牛逼牛逼
雪    币: 182
活跃值: (198)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
老G 2018-11-20 11:23
3
0
厉害厉害
游客
登录 | 注册 方可回帖
返回