2021 祥云杯 RE dizzy WP

这道题 其实是一道模拟题 作者估计也没想到 被hexRay背刺了 大概思想是模拟了 加 减 异或三种运算 可惜模拟的子片段能被hexRay识别并分析,导致把maxFunSize参数上限调高后,ida直接把运算式生成了:

byte_43841C[6] += byte_43841C[3];
byte_43841C[10] -= byte_43841C[27];
byte_43841C[10] += byte_43841C[6];
byte_43841C[14] += byte_43841C[3];
byte_43841C[28] ^= byte_43841C[24];
byte_43841C[30] += 33;
byte_43841C[12] += 33;
byte_43841C[24] ^= byte_43841C[21];
byte_43841C[22] += 33;
byte_43841C[25] += byte_43841C[10];
byte_43841C[4] += byte_43841C[6];
byte_43841C[12] += 33;
byte_43841C[17] += byte_43841C[0];
byte_43841C[20] += byte_43841C[17];

这就导致解该题 只需要复制出去,反转运算符即可
弱弱吐槽一句(其实作者的模拟汇编片段还是有若干变种的,无奈hexRay太过强大,不愧是价值几万刀的插件)

这里我用两种常见思路解决该题

angr

第一反应肯定是angr,你这不是有几千个运算吗,让angr跑就是了,中间有一个check,跟结果无关,直接hook,pass. 结尾check正确与否的用相同字符个数是不是32,不能直接用angr,会路径爆炸,所以也hook掉,编写我们的模拟check,还有程序开了alsr,让angr在固定位置加载:

import angr
import claripy
import sys

def main(argv):
  path_to_binary = argv[1]
  project = angr.Project(path_to_binary,main_opts={'base_addr':0x400000})
  start_address = 0x4011C3
  initial_state = project.factory.blank_state(addr = start_address)

  userin = claripy.BVS('userin',256)
  userin_address = 0x43841C
  initial_state.memory.store(userin_address,userin)

  @project.hook(0x0043306F,length=0x004330C0-0x0043306F)
  def skip_puts(state):
    pass

  check_equals_called_address = 0x00434130
  instruction_to_skip_length = 0x004341FD - 0x00434130
  @project.hook(check_equals_called_address, length=instruction_to_skip_length)
  def skip_check_equals_(state):
    user_input_buffer_address = 0x43841c
    user_input_buffer_length = 0x20

    user_input_string = state.memory.load(
      user_input_buffer_address, 
      user_input_buffer_length
    )
    
    v5buff = [0x27, 0x3C, 0xE3, 0xFC, 0x2E, 0x41, 0x07, 0x5E, 0x62, 0xCF, 0xE8, 0xF2, 0x92, 0x80, 0xE2, 0x36, 0xB4, 0xB2, 0x67, 0x77, 0x0F, 0xF6, 0x0D, 0xB6, 0xED, 0x1C, 0x65, 0x8A, 0x07, 0x53, 0xA6, 0x66]

    check_against_string = bytes(v5buff)
    print(user_input_string)
    state.regs.eip = claripy.If(
      user_input_string == check_against_string, 
      claripy.BVV(0x004341FD, 32), 
      claripy.BVV(0x0043420D, 32)
    )
  

  simulation = project.factory.simgr(initial_state)

  simulation.explore(find=0x00434202, avoid=0x00434212)

  if simulation.found:
    solution_state = simulation.found[0]
    solution = solution_state.solver.eval(userin, cast_to=bytes).decode('utf-8')
    print (solution)

if __name__ == '__main__':
  main(sys.argv)

不过很可惜 作者很聪明,加了大量的异或运算,导致生成的式子Z3跑不出来 angr貌似处理异或时不是很聪明,可能退化为枚举了

capstone

思路是,用capstone 反汇编程序,提取其中的子函数片段, 然后匹配我们分析的几种pattern, 模拟逆向运算过程


import capstone

simulate_stack = []

with open('Dizzy.exe','rb') as f:
    buff = f.read()

CODE = buff[0x1c3+0x400:0x3206f+0x400] + buff[0x320c0+0x400:0x33130+0x400] + b'#'

pos = 0

md = capstone.Cs(capstone.CS_ARCH_X86,capstone.CS_MODE_64)
md.detail = True

def parserDouble():
    global pos
    code = CODE[pos:pos+46]
    index = 0
    imm1 = 0
    imm2 = 0
    op = ''
    #print(code)
    for i in md.disasm(code, 0): 
        #print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
        if(i.insn_name() == 'shl' and i.size == 2):
            pos -= 1
        if(index == 1):
            if(i.insn_name() == 'shl'):
                imm1 = 2 ** i.operands[1].imm
            else:
                imm1 = i.operands[2].imm
        if(index == 3):
            if(i.insn_name() == 'shl'):              
                imm2 = 2 ** i.operands[1].imm
            else:
                imm2 = i.operands[2].imm
        if(index == 6):
            op = i.insn_name()
        index = index + 1
    return (0,imm1,imm2,op)

def parserSingle():
    global pos
    code = CODE[pos:pos+32]
    index = 0
    imm1 = 0
    imm2 = 0
    op = ''
    for i in md.disasm(code, 0):
        if(i.insn_name() == 'shl' and i.size == 2):
            pos -= 1
        #print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))  
        if(index == 1):
            if(i.insn_name() == 'shl'):  
                imm1 = 2 ** i.operands[1].imm
            else:
                imm1 = i.operands[2].imm
        if(index == 3):
            imm2 = i.operands[1].imm
            op = i.insn_name()
        index = index + 1
    return (1,imm1,imm2,op)

def check_pattern():
    global pos
    code = CODE[pos:pos + 18]
    index = 0
    for i in md.disasm(code,0):
        if(index == 2):
            if(i.insn_name() == 'movzx'):
                return 'single'
            else:
                return 'double'
        index = index + 1

while True:
    if( CODE[pos] != ord('#') and check_pattern() == 'double'):
        res =  parserDouble()
        #print(res)
        pos += 46
        simulate_stack.append(res)

    if(CODE[pos] != ord('#') and check_pattern() == 'single'):
        res = parserSingle()
        #print(res)
        pos += 32
        simulate_stack.append(res)

    if(CODE[pos] == ord('#')):
        break

cipher = [0x27, 0x3C, 0xE3, 0xFC, 0x2E, 0x41, 0x07, 0x5E, 0x62, 0xCF, 0xE8, 0xF2, 0x92, 0x80, 0xE2, 0x36, 0xB4, 0xB2, 0x67, 0x77, 0x0F, 0xF6, 0x0D, 0xB6, 0xED, 0x1C, 0x65, 0x8A, 0x07, 0x53, 0xA6, 0x66]


# f = open('temper.txt','w')
# target_write = []

while(len(simulate_stack) != 0):
    pattern,imm1,imm2,op = simulate_stack.pop()

    # target_write.append('{} {} {} {}\n'.format(pattern,imm1,imm2,op))

    if pattern == 0:
        if op == 'add':
            cipher[imm1] -= cipher[imm2]
        if op == 'sub':
            cipher[imm1] += cipher[imm2]
        if op == 'xor':
            cipher[imm1] ^= cipher[imm2]
    if pattern == 1:
        if op == 'add':
            cipher[imm1] -= imm2
        if op == 'sub':
            cipher[imm1] += imm2
        if op == 'xor':
            cipher[imm1] ^= imm2

# f.writelines(target_write)
flag = ''
for ch in cipher:
    flag += chr(ch%256)
print(flag)

结果:

[Running] python -u “c:\Users\fwk\Desktop\solve_capstone.py”
flag{Try_R3vers1ng_W1th_ScR!pt!}
[Done] exited with code=0 in 0.66 seconds

取模是为了模拟char数据类型
也可以将代码注释去掉,生成一个中间操作文件,再用c语言读取模拟操作:

0 16 20 add

0 0 5 add

1 21 54 sub

0 22 31 sub

0 29 25 add

0 18 14 xor

C语言解析执行如下:

#include <stdio.h>
#include <string.h>
unsigned char cipher[] = {0x27, 0x3C, 0xE3, 0xFC, 0x2E, 0x41, 0x07, 0x5E, 0x62, 0xCF, 0xE8, 0xF2, 0x92, 0x80, 0xE2, 0x36, 0xB4, 0xB2, 0x67, 0x77, 0x0F, 0xF6, 0x0D, 0xB6, 0xED, 0x1C, 0x65, 0x8A, 0x07, 0x53, 0xA6, 0x66};
int main()
{
    int pattern;
    int imm1;
    int imm2;
    char op[64];
    FILE* fp = fopen("temper.txt","r");
    while(fscanf(fp,"%d %d %d %s",&pattern,&imm1,&imm2,op)!=EOF)
    {
        if (pattern == 0)
        {
            if (!strcmp(op,"add"))
                cipher[imm1] -= cipher[imm2];
            if (!strcmp(op,"sub"))
                cipher[imm1] += cipher[imm2];
            if (!strcmp(op,"xor"))
                cipher[imm1] ^= cipher[imm2];
        }
        else
        {
            if (!strcmp(op,"add"))
                cipher[imm1] -= imm2;
            if (!strcmp(op,"sub"))
                cipher[imm1] += imm2;
            if (!strcmp(op,"xor"))
                cipher[imm1] ^= imm2;
        }

    }
    printf("%s",cipher);
    return 0;
}

结果:

[Running] cd “c:\Users\fwk\Desktop” && g++ final_calc.cpp -o final_calc && "c:\Users\fwk\Desktop"final_calc
flag{Try_R3vers1ng_W1th_ScR!pt!}�.@
[Done] exited with code=0 in 0.236 seconds

人工分析出的pattern:

0x004011C3 -- 0x0043306F [)          
0x004330C0  -- 00434130  [)

0x400
0x1c3 -- 0x 3206f 
0x 320c0 -- 0x 33130        
1:

buff[a] += buff[b]
.text:004011C3                 mov     eax, 1
.text:004011C8                 imul    ecx, eax, 6
.text:004011CB                 mov     edx, 1
.text:004011D0                 imul    eax, edx, 3
.text:004011D3                 movzx   edx, byte_43841C[eax]
.text:004011DA                 movzx   eax, byte_43841C[ecx]
.text:004011E1                 add     eax, edx
.text:004011E3                 mov     ecx, 1
.text:004011E8                 imul    edx, ecx, 6
.text:004011EB                 mov     byte_43841C[edx], al

2:
buff[a] -= buff[b]
.text:004011F1                 mov     eax, 1
.text:004011F6                 imul    ecx, eax, 0Ah
.text:004011F9                 mov     edx, 1
.text:004011FE                 imul    eax, edx, 1Bh
.text:00401201                 movzx   edx, byte_43841C[eax]
.text:00401208                 movzx   eax, byte_43841C[ecx]
.text:0040120F                 sub     eax, edx
.text:00401211                 mov     ecx, 1
.text:00401216                 imul    edx, ecx, 0Ah
.text:00401219                 mov     byte_43841C[edx], al

3:
buff[a] ^= buff[b]
.text:0040127B                 mov     eax, 1
.text:00401280                 imul    ecx, eax, 1Ch
.text:00401283                 mov     edx, 1
.text:00401288                 imul    eax, edx, 18h
.text:0040128B                 movzx   edx, byte_43841C[eax]
.text:00401292                 movzx   eax, byte_43841C[ecx]
.text:00401299                 xor     eax, edx
.text:0040129B                 mov     ecx, 1
.text:004012A0                 imul    edx, ecx, 1Ch
.text:004012A3                 mov     byte_43841C[edx], al

4:
buff[a] += imm
.text:004012A9                 mov     eax, 1
.text:004012AE                 imul    ecx, eax, 1Eh
.text:004012B1                 movzx   edx, byte_43841C[ecx]
.text:004012B8                 add     edx, 21h ; '!'
.text:004012BB                 mov     eax, 1
.text:004012C0                 imul    ecx, eax, 1Eh
.text:004012C3                 mov     byte_43841C[ecx], dl


第9 :0x0F


.text:00401365 B8 01 00 00 00                          mov     eax, 1
.text:0040136A C1 E0 02                                shl     eax, 2
.text:0040136D B9 01 00 00 00                          mov     ecx, 1
.text:00401372 6B D1 06                                imul    edx, ecx, 6
.text:00401375 0F B6 8A 1C 84 43 00                    movzx   ecx, byte_43841C[edx]
.text:0040137C 0F B6 90 1C 84 43 00                    movzx   edx, byte_43841C[eax]
.text:00401383 03 D1                                   add     edx, ecx
.text:00401385 B8 01 00 00 00                          mov     eax, 1
.text:0040138A C1 E0 02                                shl     eax, 2
.text:0040138D 88 90 1C 84 43 00                       mov     byte_43841C[eax], dl
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值