天堂之门
学习文章 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
读硬编码:
EA 10 53 C7 00 33 00
jmp 0033:00C75310(cs:ip)
以这个长跳转便改变了cs的值位0x33.之后便会以64位执行下面call的函数
64位转32位
用retf实现
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 = muself.except_addr = 0self.except_hit = except_hitself.traces = []self.hit = 0self.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.mnemonicif 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 += 1if self.hit == self.except_hit:self.success = Truemu.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:passreturn self.successdef get_flag(flag, except_hit):for i in b'1234567890abcdefABCDEF':for j in b'1234567890abcdefABCDEF':flag[8 + (except_hit - 1) * 2] = iflag[8 + (except_hit - 1) * 2 + 1] = jif UniVM(bytes(flag), except_hit).solve():returnflag = 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