[天堂之门+模拟执行]2021羊城杯oddcode

天堂之门

学习文章 https://taardisaa.github.io/2021/09/25/Heaven'sGate/

前言

Windows判别位的方式,是根据cs段寄存器的。

在32位程序中,cs的值是0x23;

在64位程序中,cs的值是0x33。

所以只要修改cs的值,就能实现切换。而实现这个切换,使用的是ljmp长跳转指令(在Intel语法中也可以叫做jmp far ptr),或者retf长返回指令.

32位转64位

以jmp far ptr xxxx对应jmp xx:xxxx
img
读硬编码:
EA 10 53 C7 00 33 00
jmp 0033:00C75310(cs:ip)
以这个长跳转便改变了cs的值位0x33.之后便会以64位执行下面call的函数

64位转32位

用retf实现
img
call到下一条指令,并将这条指令的地址压栈.
call $+5
将栈顶esp的上一个数赋值为0x23
mov dword ptr [esp+4], 23h
将栈顶esp进行修正.将返回地址设置到retf后一共0x874331-0x87431A=0x17(从mov到retf)
sub dword ptr [esp], 87431Ah
add dword ptr [esp], 874331h
最好retf依次从栈顶弹出ip的值与cs的值,cs:ip共同作用实现返回

模拟执行(Unicorn)

先贴一下exp
import capstone
from unicorn import *
from unicorn.x86_const import *

ADDRESS = 0xC71000  # 初始地址
INPUT_ADDR = 0xc7701d
KEY_ADDR = 0xc7705c
with open(r"D:\ctf\ctf_game\OddCode.exe", 'rb') as fp:
    fp.seek(0x400)
    x64_code = fp.read(0x5000)


class UniVM:

    def __init__(self, flag, except_hit):
        # 创建虚拟机(x86架构,64位)
        mu = Uc(UC_ARCH_X86, UC_MODE_64)
        # 分配内存
        mu.mem_map(ADDRESS, 0x1000000)
        # 填充内存
        mu.mem_write(ADDRESS, x64_code)
        # 填充寄存器
        mu.mem_write(INPUT_ADDR, flag)
        mu.mem_write(KEY_ADDR, b'\x90\xF0\x70\x7C\x52\x05\x91\x90\xAA\xDA\x8F\xFA\x7B\xBC\x79\x4D')

        mu.reg_write(UC_X86_REG_RAX, 1)
        mu.reg_write(UC_X86_REG_RBX, 0xf1b02d)
        mu.reg_write(UC_X86_REG_RCX, 0x5779887a)
        mu.reg_write(UC_X86_REG_RDX, 1)
        mu.reg_write(UC_X86_REG_RSI, INPUT_ADDR)
        mu.reg_write(UC_X86_REG_RDI, KEY_ADDR)
        mu.reg_write(UC_X86_REG_RBP, 0x118fe44)
        mu.reg_write(UC_X86_REG_RSP, 0x118fe34)
        mu.reg_write(UC_X86_REG_RIP, 0xC75309)
        mu.reg_write(UC_X86_REG_RFLAGS, 0x202)

        # mu.hook_add(UC_HOOK_MEM_READ, self.hook_mem_read)

        mu.hook_add(UC_HOOK_CODE, self.trace)

        self.md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
        self.mu = mu
        self.except_addr = 0
        self.except_hit = except_hit
        self.traces = []
        self.hit = 0
        self.success = False

    '''
    #capstone查看cmp与test位置
    def trace(self, mu, address,size, data):
        disasm = self.md.disasm(mu.mem_read(address, size), address)
        for i in disasm:
            mnemonic = i.mnemonic
            if mnemonic == 'cmp' or mnemonic == 'test':
                print(f'Instruction{mnemonic} at {hex(address)}')
        if address != self.except_addr:
        self.traces.append(address)
        self.except_addr = address+size
    '''

    def trace(self, mu, address, size, data):
        if address != self.except_addr:
            self.traces.append(address)
        self.except_addr = address + size  # 更新 self.except_addr 为当前指令结束后的地址(address + size)
        if address == 0xc738EF:
            self.hit += 1

            if self.hit == self.except_hit:
                self.success = True
                mu.emu_stop()

    def hook_mem_read(self, mu, access, address, size, value, data):
        if address >= INPUT_ADDR and address <= INPUT_ADDR + 41:
            print(f'Read input[{address - INPUT_ADDR}] at {hex(mu.reg_read(UC_X86_REG_RIP))}')
        if address >= KEY_ADDR and address <= KEY_ADDR + 16:
            print(f'Read key[{address - KEY_ADDR}] at {hex(mu.reg_read(UC_X86_REG_RIP))}')

    def solve(self):
        try:
            self.mu.emu_start(ADDRESS + 0x10, -1)
        except:
            pass
        return self.success


def get_flag(flag, except_hit):
    for i in b'1234567890abcdefABCDEF':
        for j in b'1234567890abcdefABCDEF':
            flag[8 + (except_hit - 1) * 2] = i
            flag[8 + (except_hit - 1) * 2 + 1] = j
            if UniVM(bytes(flag), except_hit).solve():
                return

flag = bytearray(b'SangFor{00000000000000000000000000000000}')
for i in range(1, 17):
    get_flag(flag, i)
    print(flag.decode())

基本使用方法: https://www.cnblogs.com/Only-xiaoxiao/p/17316343.html

posted @ 2024-07-17 07:40  Un1corn  阅读(12)  评论(0编辑  收藏  举报