#是的一道经典的题目。(留着以后招新用了)

##面向异常的编程,可能是目前首选的软件保护方式了,它完美的对抗了反汇编分析和符号执行分析,是一道较困难题目。

题目: 面向异常编程

首先放上,.bss段主要全局变量我idb的命名。

首先程序读入长度为48的flag。
程序通过不断throw Exception来通过catch Exception,在catch中实现对flag的加密,最终与特定数据通过memcmp进行比较,

可以看到在main中,一个典型的for(int i=0;i<0x30;i += 0x10)的循环,每轮循环对flag的16位进行加密,不断调用其中encrypThroughException函数。

在这个函数里逻辑:

难点在于其通过堆中保存的函数偏移,来间接调用加密函数,需要动态分析程序具体是调用什么样的函数(这个过程很复杂)。

##加密函数第一个(异或input1)


for(int i=0;i<0xF;i++){ mov eax, [rbp+i] movsxd rdx, eax ; Move with Sign-Extend Doubleword lea rax, input_1 ; Load Effective Address movzx ecx, byte ptr [rdx+rax] ; Move with Zero-Extend mov eax, [rbp+i] movsxd rdx, eax ; Move with Sign-Extend Doubleword lea rax, temp_qword1 ; Load Effective Address movzx eax, byte ptr [rdx+rax] ; Move with Zero-Extend xor ecx, eax ; Logical Exclusive OR mov eax, [rbp+i] movsxd rdx, eax ; Move with Sign-Extend Doubleword lea rax, input_1 ; dword mov [rdx+rax], cl add [rbp+i], 1 ; Add jmp short loc_55BC47CC390A ; Jump } //其逻辑是这样 for(int j=0;j<=0xF;j++){ input_1[j] = input_1[j]^temp_dword1[j] }

##加密函数第二个(第一波)


mov rdi, rax call ___cxa_begin_catch ; Call Procedure movzx eax, byte ptr cs:input_1 ; Move with Zero-Extend movzx eax, al ; Move with Zero-Extend movzx edx, byte ptr cs:input_1+1 ; Move with Zero-Extend movzx edx, dl ; Move with Zero-Extend shl edx, 8 ; Shift Logical Left or edx, eax ; Logical Inclusive OR movzx eax, byte ptr cs:input_1+2 ; Move with Zero-Extend movzx eax, al ; Move with Zero-Extend shl eax, 10h ; Shift Logical Left or edx, eax ; Logical Inclusive OR movzx eax, byte ptr cs:input_1+3 ; Move with Zero-Extend movzx eax, al ; Move with Zero-Extend shl eax, 18h ; Shift Logical Left or edx, eax ; Logical Inclusive OR mov rax, cs:xor_data_off mov eax, [rax] xor eax, edx ; Logical Exclusive OR mov cs:encryDword1, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 6Ah ; 'j' mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

其取出input_1的四位(不如说是DWORD类型值),与xor_data_off的Dword类型进行异或,写入encryptedDword1.

##加密函数第三个

call ___cxa_begin_catch ; Call Procedure
movzx eax, byte ptr cs:input_1+4 ; Move with Zero-Extend
movzx eax, al ; Move with Zero-Extend
movzx edx, byte ptr cs:input_1+5 ; Move with Zero-Extend
movzx edx, dl ; Move with Zero-Extend
shl edx, 8 ; Shift Logical Left
or edx, eax ; Logical Inclusive OR
movzx eax, byte ptr cs:input_1+6 ; Move with Zero-Extend
movzx eax, al ; Move with Zero-Extend
shl eax, 10h ; Shift Logical Left
or edx, eax ; Logical Inclusive OR
movzx eax, byte ptr cs:input_1+7 ; Move with Zero-Extend
movzx eax, al ; Move with Zero-Extend
shl eax, 18h ; Shift Logical Left
or edx, eax ; Logical Inclusive OR
mov rax, cs:xor_data_off
mov eax, [rax+4]
xor eax, edx ; Logical Exclusive OR
mov cs:encryDword2, eax
mov edi, 4
call ___cxa_allocate_exception ; Call Procedure
mov dword ptr [rax], 6Dh ; 'm'
mov edx, 0
lea rsi, _ZTIi ; `typeinfo for'int
mov rdi, rax
call ___cxa_throw ; Call Procedure

与上一个函数逻辑相同,这个取出input_1的四位,与xor_data_off的第二个Dword类型进行异或,写入encryptedData2

##加密函数第四个
##加密函数第五个
与上两个函数逻辑相同,只是对input_2操作,写入的是encryptedData3,encryptedData4.

##加密函数第六个(第二波)

mov rdi, rax
call ___cxa_begin_catch ; Call Procedure
mov rax, cs:xor_data_off
mov edx, cs:encryDword1
movzx edx, dl ; &0xFF
mov edx, edx
add rdx, 40h ; Add
mov edx, [rax+rdx*4]
mov rax, cs:xor_data_off
mov ecx, cs:encryDword1
shr ecx, 8 ; Shift Logical Right
movzx ecx, cl ; Move with Zero-Extend
mov ecx, ecx
add rcx, 140h ; Add
mov eax, [rax+rcx*4]
mov ecx, edx
xor ecx, eax ; Logical Exclusive OR
mov rax, cs:xor_data_off
mov edx, cs:encryDword1
shr edx, 10h ; Shift Logical Right
movzx edx, dl ; Move with Zero-Extend
mov edx, edx
add rdx, 240h ; Add
mov eax, [rax+rdx*4]
xor ecx, eax ; Logical Exclusive OR
mov rax, cs:xor_data_off
mov edx, cs:encryDword1
shr edx, 18h ; Shift Logical Right
mov edx, edx
add rdx, 340h ; Add
mov eax, [rax+rdx*4]
xor eax, ecx ; Logical Exclusive OR
mov cs:encryDword5, eax
mov edi, 4
call ___cxa_allocate_exception ; Call Procedure
mov dword ptr [rax], 24h ; '$'
mov edx, 0
lea rsi, _ZTIi ; `typeinfo for'int
mov rdi, rax
call ___cxa_throw ; Call Procedure

##加密函数第七个与上述类似

这两段比较长,意思就是这样的,伪代码表示


for(int i=1;i<=2;i++){ temp = encrypedData[i] temp2 = xor_data_off[4*(temp&0xFF+0x40)] temp >>= 8 for(int j=1;j<4;j++){ temp2 ^= xor_data_off[4*(temp&0xFF+0x40+j*0x100)] temp >>= 8 } encryDword[4+i] = temp2 }

#加密函数第九段


mov rdi, rax call ___cxa_begin_catch ; Call Procedure mov edx, cs:encryDword5 mov eax, cs:encryDword6 add edx, eax ; Add mov rax, cs:xor_data_off mov eax, [rax+20h] add edx, eax ; Add mov eax, cs:encryDword3 xor eax, edx ; Logical Exclusive OR mov cs:encryDword3, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 66h ; 'f' mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

上面第八段和第七段对encryDword5和encryDword6进行了初始化,这里函数使用两个数据对encryDword3进行修改。
这段意思:


encryDword3 ^= (encryDword5+encryDword6+xor_data_off[0x20h])

##加密函数第十段


mov rdi, rax call ___cxa_begin_catch ; Call Procedure mov eax, cs:encryDword6 lea edx, [rax+rax] ; Load Effective Address mov eax, cs:encryDword5 add edx, eax ; Add mov rax, cs:xor_data_off mov eax, [rax+24h] add edx, eax ; Add mov eax, cs:encryDword4 xor eax, edx ; Logical Exclusive OR mov cs:encryDword4, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 4Bh ; 'K' mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

encryDword4 ^= (encryDword5+encryDword6+xor_data_off[0x24h])

##加密函数第十一段


mov rdi, rax call ___cxa_begin_catch ; Call Procedure mov rax, cs:xor_data_off mov edx, cs:encryDword3 movzx edx, dl ; Move with Zero-Extend mov edx, edx add rdx, 40h ; Add mov edx, [rax+rdx*4] mov rax, cs:xor_data_off mov ecx, cs:encryDword3 shr ecx, 8 ; Shift Logical Right movzx ecx, cl ; Move with Zero-Extend mov ecx, ecx add rcx, 140h ; Add mov eax, [rax+rcx*4] mov ecx, edx xor ecx, eax ; Logical Exclusive OR mov rax, cs:xor_data_off mov edx, cs:encryDword3 shr edx, 10h ; Shift Logical Right movzx edx, dl ; Move with Zero-Extend mov edx, edx add rdx, 240h ; Add mov eax, [rax+rdx*4] xor ecx, eax ; Logical Exclusive OR mov rax, cs:xor_data_off mov edx, cs:encryDword3 shr edx, 18h ; Shift Logical Right mov edx, edx add rdx, 340h ; Add mov eax, [rax+rdx*4] xor eax, ecx ; Logical Exclusive OR mov cs:encryDword5, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 65h ; 'e' mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

##加密函数第十二段

这两段与第七段第八段逻辑相同。


for(int i=3;i<=4;i++){ temp = encrypedData[i] temp2 = xor_data_off[4*(temp&0xFF+0x40)] temp >>= 8 for(int j=1;j<4;j++){ temp2 ^= xor_data_off[4*(temp&0xFF+0x40+j*0x100)] temp >>= 8 } encryDword[2+i] = temp2 }

##加密函数第十三段


mov rdi, rax call ___cxa_begin_catch ; Call Procedure mov edx, cs:encryDword5 mov eax, cs:encryDword6 add edx, eax ; Add mov rax, cs:xor_data_off mov eax, [rax+28h] add edx, eax ; Add mov eax, cs:encryDword1 xor eax, edx ; Logical Exclusive OR mov cs:encryDword1, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 59h ; 'Y' mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

这个段与第九段第十段相同,执行后同样执行unwind。


encryDword1 ^= (encryDword5+encryDword6+xor_data_off[0x28h])

##加密函数第十四段


mov rdi, rax call ___cxa_begin_catch ; Call Procedure mov eax, cs:encryDword1 ror eax, 1 ; Rotate Right mov cs:encryDword1, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 1Fh mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

对encryptedData1进行了RAR 一位

##加密函数第十五段


mov rdi, rax call ___cxa_begin_catch ; Call Procedure mov eax, cs:encryDword2 rol eax, 1 ; Rotate Left mov cs:encryDword2, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 13h mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

对encryptedData2进行了RAL 一位

##加密函数第十六段


call ___cxa_begin_catch ; Call Procedure mov eax, cs:encryDword6 lea edx, [rax+rax] ; Load Effective Address mov eax, cs:encryDword5 add edx, eax ; Add mov rax, cs:xor_data_off mov eax, [rax+2Ch] add edx, eax ; Add mov eax, cs:encryDword2 xor eax, edx ; Logical Exclusive OR mov cs:encryDword2, eax mov edi, 4 call ___cxa_allocate_exception ; Call Procedure mov dword ptr [rax], 37h ; '7' mov edx, 0 lea rsi, _ZTIi ; `typeinfo for'int mov rdi, rax call ___cxa_throw ; Call Procedure

encryDword2 ^= (encryDword5+encryDword6+xor_data_off[0x2Ch])

#看到这里,GOOD我们大功告成一半了。
我们发现,接下来就是跳转到第七个加密函数进行循环了。
好的我们这里再次理一下其加密逻辑:

    for(int k=0;k<0x30;k+=0x10){
        for(int j=0;j<=0xF;j++){
            input_1[j] = input_1[j]^temp_dword1[j]
        }
        for(int j=0;j<8;j++){
            for(int i=1;i<=2;i++){
                temp = encrypedData[i]
                temp2 = xor_data_off[4*(temp&0xFF+0x40)]
                temp >>= 8
                for(int j=1;j<4;j++){
                    temp2 ^= xor_data_off[4*(temp&0xFF+0x40+j*0x100)]
                    temp >>= 8
                }
                encryDword[4+i] = temp2
            }
            encryDword3 ^= (encryDword5+encryDword6+xor_data_off[0x20h])        //0x20每次大循环递增0x10
            对encryptedData3进行RAR 一位
            对encryptedData4进行RAL 一位
            encryDword4 ^= (encryDword5+encryDword6+xor_data_off[0x24h])        //0x24每次大循环递增0x10

            for(int i=3;i<=4;i++){
                temp = encrypedData[i]
                temp2 = xor_data_off[4*(temp&0xFF+0x40)]
                temp >>= 8
                for(int j=1;j<4;j++){
                    temp2 ^= xor_data_off[4*(temp&0xFF+0x40+j*0x100)]
                    temp >>= 8
                }
                encryDword[2+i] = temp2
            }


            encryDword1 ^= (encryDword5+encryDword6+xor_data_off[0x28h])        //0x28每次大循环递增0x10
            对encryptedData1进行RAR 一位
            对encryptedData2进行RAL 一位
            encryDword2 ^= (encryDword5+encryDword6+xor_data_off[0x2Ch])        //0x28每次大循环递增0x10
        }
        //这层循环结束后会取出encryDword3,encryDword4对temp_qword1进行赋值,取出encryDword1,encryDword2对temp_qword1进行赋值
    }
    //伪代码仅供参考理清思路。

#大功告成的另一半,是写出逆向脚本
得到上述逻辑,我们可以开始写脚本逆向了。


from struct import * max_bits = 32 address = 0x055EB62CA0020 xor_data = [] for i in range(4400): xor_data.append(get_wide_byte(address+i)) def access_offstream(offset, index): return array_to_dword(offset[index:index+4][::-1]) def array_to_dword(a): return int(''.join(pack('B', x) for x in a).encode('hex'),16) def shr_add_xor(dword, offset, shifts, adds=[0,0,0,0], muls=[1,1,1,1]): edx = ((dword >> shifts[0] & 0xff) + adds[0]) * muls[0] a = array_to_dword(offset[edx:edx+4][::-1]) edx = ((dword >> shifts[1] & 0xff) + adds[1])* muls[1] b = array_to_dword(offset[edx:edx+4][::-1]) c = a ^ b edx = ((dword >> shifts[2] & 0xff) + adds[2])* muls[2] d = array_to_dword(offset[edx:edx+4][::-1]) e = d ^ c edx = ((dword >> shifts[3] & 0xff) + adds[3])* muls[3] f = array_to_dword(offset[edx:edx+4][::-1]) g = f ^ e return a, b, c, d, f, g def rol(val, r_bits, max_bits): return (val << r_bits%max_bits) & (2**max_bits-1) | ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits))) def ror(val, r_bits, max_bits): return ((val & (2**max_bits-1)) >> r_bits%max_bits) | (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1)) def sum_sum_xor(dword0,dword1, dword3, offset, offset_index): sum1 = (dword0 + dword1) & 0xffffffff sum2 = (sum1 + array_to_dword(offset[offset_index:offset_index+4][::-1])) & 0xffffffff return sum2 ^ dword3 encrypted_flag = [0x4F, 0x6F, 0xA7, 0x87, 0xE9, 0x51, 0x87, 0x64, 0x38, 0x2A, 0x46, 0xE5, 0x4F, 0x21, 0x9E, 0x1C, 0xCD, 0x65, 0xE1, 0x9A, 0x4F, 0xCF, 0xDE, 0x52, 0x09, 0xBF, 0x53, 0xC4, 0xB0, 0x95, 0x75, 0x31, 0xAC, 0x2F, 0xF4, 0x97, 0x1D, 0xA5, 0x9A, 0x02, 0xA8, 0xFF, 0xAE, 0x2E, 0xB9, 0x70, 0xCC, 0x02] if __name__ == "__main__": xor_keys_input0 = [0] * 0x8 xor_keys_input1 = [0] * 0x8 for x in range(len(encrypted_flag)): if (x % 16) < 8: xor_keys_input0.append(encrypted_flag[x]) else: xor_keys_input1.append(encrypted_flag[x]) flag = '' for x in range(0,16*3,16): test = encrypted_flag[x:16+x] input_qword_1 = test[:8][::-1] input_qword_2 = test[8:][::-1] dword1 = array_to_dword(input_qword_2[-4:]) dword2 = array_to_dword(input_qword_2[:4]) dword3 = array_to_dword(input_qword_1[-4:]) dword4 = array_to_dword(input_qword_1[:4]) dword3 = dword3 ^ access_offstream(xor_data, 0x10) dword4 = dword4 ^ access_offstream(xor_data, 0x14) dword1 = dword1 ^ access_offstream(xor_data, 0x18) dword2 = dword2 ^ access_offstream(xor_data, 0x1c) for i in range(16*7,-16,-16): dword5 = shr_add_xor(dword3, xor_data, [0,8,0x10,0x18], [0x40,0x140,0x240,0x340], [4,4,4,4])[-1] dowrd6 = shr_add_xor(dword4, xor_data, [0x18,0,0x8,0x10], [0x40,0x140,0x240,0x340], [4,4,4,4])[-1] edx = dword5 + (dowrd6+dowrd6) & 0xffffffff eax = (array_to_dword(xor_data[0x2c+i:0x2c+4+i][::-1])+edx) & 0xffffffff dword2 = dword2 ^ eax dword2 = ror(dword2,1, max_bits) dword1 = rol(dword1,1, max_bits) dword1 = sum_sum_xor(dword5, dowrd6, dword1, xor_data, 0x28+i) dword5 = shr_add_xor(dword1, xor_data, [0,8,0x10,0x18], [0x40,0x140,0x240,0x340], [4,4,4,4])[-1] dowrd6 = shr_add_xor(dword2, xor_data, [0x18,0,0x8,0x10], [0x40,0x140,0x240,0x340], [4,4,4,4])[-1] edx = dword5 + ((dowrd6+dowrd6) & 0xffffffff) eax = (array_to_dword(xor_data[0x24+i:0x24+4+i][::-1])+edx) & 0xffffffff dword4 = dword4 ^ eax dword4 = ror(dword4,1, max_bits) dword3 = rol(dword3,1, max_bits) dword3 = sum_sum_xor(dword5, dowrd6, dword3, xor_data, 0x20+i) flaginput1 = [dword3,dword4] flaginput2 = [dword1,dword2] k = 0 s = '' for i in range(0,8,4): s += pack("<I",flaginput2[k] ^ access_offstream(xor_data,i)) k += 1 s = pack(">Q", int(s.encode('hex'), 16) ^ array_to_dword(xor_keys_input0[x/2:x/2+8])) s1 = '' k = 0 for i in range(8,8*2,4): s1 += pack("<I",flaginput1[k] ^ access_offstream(xor_data,i)) k += 1 s1 = pack(">Q", int(s1.encode('hex'), 16) ^ array_to_dword(xor_keys_input1[x/2:x/2+8])) flag += s + s1 print "flag{%s}" % flag #flag{~Exc3p7i0n-Ori3n7ed-Pr0grammin9~RoO0cks!!\o^_^o/}

发表评论

电子邮件地址不会被公开。