攻防世界-polyre WP 难度5
大概的流程
刚开始直接ollvm混淆吓死你
1.首先使用deflat.py去除控制流平坦化,这里给出使用示例:
(angr-dev) <path>/deflat/flat_control_flow$ python3 deflat.py -f samples/bin/check_passwd_x8664_flat --addr 0x400530 *******************relevant blocks************************ prologue: 0x400530 main_dispatcher: 0x400554 pre_dispatcher: 0x40099b retn: 0x40098f relevant_blocks: ['0x40086a', '0x40080d', '0x4008ee', '0x40094f', '0x40084e', '0x400819', '0x400886', '0x40095b', '0x4007ec', '0x40092e', '0x4008a9', '0x4008cc', '0x40091b', '0x40097c', '0x400837'] *******************symbolic execution********************* -------------------dse 0x40086a--------------------- -------------------dse 0x40080d--------------------- -------------------dse 0x4008ee--------------------- -------------------dse 0x40094f--------------------- -------------------dse 0x40084e--------------------- -------------------dse 0x400819--------------------- -------------------dse 0x400886--------------------- -------------------dse 0x40095b--------------------- -------------------dse 0x4007ec--------------------- -------------------dse 0x40092e--------------------- -------------------dse 0x4008a9--------------------- -------------------dse 0x4008cc--------------------- -------------------dse 0x40091b--------------------- -------------------dse 0x40097c--------------------- -------------------dse 0x400837--------------------- -------------------dse 0x400530--------------------- ************************flow****************************** 0x40084e: ['0x40086a', '0x40095b'] 0x40086a: ['0x400886', '0x40094f'] 0x400530: ['0x4007ec'] 0x4008a9: ['0x4008cc', '0x40094f'] 0x400886: ['0x4008a9', '0x40094f'] 0x4007ec: ['0x400819', '0x40080d'] 0x40091b: ['0x40098f'] 0x40080d: ['0x40084e'] 0x40092e: ['0x40094f'] 0x4008ee: ['0x40091b', '0x40092e'] 0x400819: ['0x400837'] 0x40094f: ['0x40097c'] 0x40095b: ['0x40097c'] 0x40097c: ['0x40098f'] 0x400837: ['0x4007ec'] 0x4008cc: ['0x4008ee', '0x40094f'] 0x40098f: [] ************************patch***************************** Successful! The recovered file: check_passwd_flat_recovered
2.这个时候我们就可以得到第一步去混淆的代码,但是仔细看这里的代码还是有点不够清晰,所以我们接着动调,看看函数的逻辑是怎么执行的。
3.注意这个地方的代码,这里的条件是恒不成立的,意味着我们可以修改汇编中的代码,改变函数跳转的逻辑从而继续去除混淆。
3.我们跟踪进汇编看看,可以发现有个jnz指令,并且每次都会执行这个指令,我们干脆直接改为jmp,来简化流程。
4.使用ida python脚本(以后要着重学习一下)
st = 0x0000000000400620 #main开始 end = 0x0000000000402144 #main结束 def patch_nop(start,end): for i in range(start,end): ida_bytes.patch_byte(i, 0x90) #修改指定地址处的指令 0x90是最简单的1字节nop def next_instr(addr): return addr+idc.get_item_size(addr) #获取指令或数据长度,这个函数的作用就是去往下一条指令 addr = st while(addr<end): next = next_instr(addr) if "ds:dword_603054" in GetDisasm(addr): #GetDisasm(addr)得到addr的反汇编语句 while(True): addr = next next = next_instr(addr) if "jnz" in GetDisasm(addr): dest = idc.get_operand_value(addr, 0) #得到操作数,就是指令后的数 ida_bytes.patch_byte(addr, 0xe9) #0xe9 jmp后面的四个字节是偏移 ida_bytes.patch_byte(addr+5, 0x90) #nop第五个字节 offset = dest - (addr + 5) #调整为正确的偏移地址 也就是相对偏移地址 - 当前指令后的地址 ida_bytes.patch_dword(addr + 1, offset) #把地址赋值给jmp后 print("patch bcf: 0x%x"%addr) addr = next break else: addr = next
这里详细讲一下
idc.get_operand_value(addr, 0) #获取操作数的值
应该是得到jnz指令后面标号的地址。
然后就可以得到进一步去混淆的代码了。
5.跳转仍然有些奇葩,接下来交给我们万能的gpt大人。
for (i = 0; i < 64; ++i) { if (s[i] == '\n') { s[i] = '\0'; break; } } for (j = 0; j < 6; ++j) { int64_t v = *(_QWORD *)&s[8 * j]; // 取 8 字节数据 for (k = 0; k < 64; ++k) { if (v < 0) v = (v * 2) ^ 0xB0004B7679FA26B3LL; // 负数时异或 else v *= 2; // 正数时左移 } *(_QWORD *)&s1[8 * j] = v; // 存入结果 }
6.解密脚本
s=[0xBC8FF26D43536296, 0x520100780530EE16, 0x4DC0B5EA935F08EC,0x342B90AFD853F450, 0x8B250EBCAA2C3681, 0x55759F81A2C68AE4] res="" key=0xB0004B7679FA26B3 for item in s: for j in range(64): f=item&1 if f==1: item=(item^key)//2 item = item | 0x8000000000000000 else: item=item//2 n=hex(item)[2:] print(hex(item)) for j in range(len(n)-1,-1,-2): res += chr(int("0x" + n[j-1:j + 1], 16)) print(res)
cpp的时候要注意用unsigned标号,不然会有点怪。
#include <iostream> #include <cstdint> using namespace std; const unsigned long long XOR_CONST = 0xB0004B7679FA26B3LL; unsigned long long reverse_hash(unsigned long long hashed_value) { for (int k = 63; k >= 0; --k) { // 逆向 64 轮 int f = hashed_value & 1; // 取最低位 if (f == 1) { hashed_value = (hashed_value ^ XOR_CONST) / 2; hashed_value |= 0x8000000000000000LL; // 强制恢复符号位 } else { hashed_value /= 2; } } printf("hashed_value: %llx\n", hashed_value); return hashed_value; } int main() { unsigned long long s1[6] = { 0xBC8FF26D43536296, 0x520100780530EE16, 0x4DC0B5EA935F08EC, 0x342B90AFD853F450, 0x8B250EBCAA2C3681, 0x55759F81A2C68AE4 }; char res[48] = { 0 }; for (int j = 0; j < 6; ++j) { unsigned long long original_value = reverse_hash(s1[j]); memcpy(res + j * 8, &original_value, 8); // 复制 8 字节数据 } cout << "恢复的字符串: " << res << endl; return 0; }
7.得到最终的flag
0x6666367b67616c66 0x63362d3039333932 0x2d363563342d3032 0x3539612d30376162 0x6631643365383537 0x7d38 flag{6ff29390-6c20-4c56-ba70-a95758e3d1f8}
参考链接 https://blog.csdn.net/weixin_52369224/article/details/122524328
Comments NOTHING