强网杯赛题解析 | unicorn_like_a_pro
1 • 符号还原
IDA 中的 bindiff 插件,载入另外一份 idb 后,按下 Ctrl + 6,点击如下按钮,设置好阀值后即可导入符号。
2 •main 函数分析
uc_open(4u, 8, &v14); // UC_ARCH_X86, UC_MODE_64
uc_mem_write(v14, 0x1000LL, &code, 0x1027LL);
2.3 设置回调
Unicorn 支持很多种回调类型,当 Unicorn 运行的代码满足特定的条件时,将触发对应的回调。
在回调中可以对虚拟机中环境上下文操作,类似调试器的调试回调。
本题借助 Unicorn 指令回调,实现指令即时解密,执行后重新加密,打乱控制流。
// 虚拟机输入接口,虚拟机代码中的 in 指令
uc_hook_add(v14, &trace, 2, input + 1, 0LL, 1LL, 0LL, 0xDAu);// 2 == UC_HOOK_INSN
// 虚拟机输出接口,虚拟机中代码中的 out 指令处理
uc_hook_add(v14, &trace, 2, output, 0LL, 1LL, 0LL, 0x1F4u);// 2 = UC_HOOK_INSN
// 虚拟机中 syscall 指令处理,设置 rax 寄存器的值为 time(0)
uc_hook_add(v14, &trace, 2, syscall_time, 0LL, 1LL, 0LL, 0x2BBu);// 2 = UC_HOOK_INSN
// 虚拟机中 fs 内存访问处理,改变 fs:0 的值,关键算法部分很重要
uc_hook_add(v14, &trace, 1024, changeKey, 0LL, 0x66660000LL, 0x66661000LL, v4);// UC_HOOK_MEM_READ
// 代码解密回调
uc_hook_add(v14, &trace, 8, decrypt, &v21, 1LL, 0LL, v5);// UC_HOOK_BLOCK
// 代码控制流控制回调1
uc_hook_add(v14, &trace, 0x4000, ControlFlow1, &v21, 1LL, 0LL, v6);// UC_HOOK_INSN_INVALID
// 代码控制流控制回调2
uc_hook_add(v14, &trace, 4, ControlFlow2, &v21, 0x10A3LL, 0x10A4LL, v7);// UC_HOOK_CODE
3 • 代码解密分析
v9 = miniDec(lastKey, entry_rip);
for ( i = 0; i <= 85; ++i )
{
if ( v9 == *&size_table[8 * i] )
{
size[0] = *&size_table[8 * i + 4]; // getlen
v12 = malloc(size[0]);
uc_mem_read(v5, addr, v12, size[0]);
for ( j = 0; size[0] > j; ++j )
;
dec1(v12, size[0], lastKey);
uc_mem_write(v5, addr, v12, size[0]);
for ( k = 0; size[0] > k; ++k )
;
free(v12);
*(*&size[1] + 4LL) = addr;
*(*&size[1] + 12LL) = **&size[1];
*(*&size[1] + 8LL) = size[0];
*(*&size[1] + 16LL) = addr;
uc_reg_write(v5, 41, &addr); // UC_X86_REG_RIP
}
}
__int64 __fastcall miniDec(int a1, int a2)
{
return a2 ^ a1 ^ (a2 * a1) ^ (a1 + a2);
}
dd key
dd size
size_table = [0x02F73020, 0x00000015, 0x09D3473A, 0x00000051, 0x0EF87B55, 0x0000000D, 0x147CB028, 0x00000023, 0x15F833AA, 0x00000030, 0x17086780, 0x00000018, 0x1733A9D4, 0x00000014, 0x17D61EE8, 0x00000051, 0x1D52F19E, 0x00000011, 0x1F732DE0, 0x0000000D, 0x1FBECFAD, 0x0000001B, 0x245BD7C8, 0x00000055, 0x25E7ABEE, 0x00000009, 0x2882C190, 0x000000A2, 0x2A2084A0, 0x00000075, 0x326AA6AE, 0x00000036, 0x33074A36, 0x00000024, 0x3440BD69, 0x0000002C, 0x362A1FC3, 0x0000002C, 0x3C0450D0, 0x0000000D, 0x3CB575FD, 0x00000011, 0x41B3B26E, 0x0000004E, 0x46005120, 0x00000011, 0x465A72CF, 0x00000002, 0x492145A0, 0x0000000D, 0x49AA4CE0, 0x0000002D, 0x4BD63647, 0x0000004E, 0x4BF84A87, 0x0000000D, 0x4D102445, 0x00000033, 0x4D4D3C55, 0x0000001B, 0x53723232, 0x0000000A, 0x5809B5CB, 0x000000A2, 0x5B12FFCE, 0x00000015, 0x5B1F3000, 0x00000051, 0x5D9FBD20, 0x00000027, 0x6219EED9, 0x0000008A, 0x65D82D17, 0x0000004C, 0x67F5671A, 0x00000063, 0x6CE2CBC1, 0x00000033, 0x718A739C, 0x0000000B, 0x71A62DD7, 0x00000015, 0x7693A1F6, 0x00000014, 0x7A473FB0, 0x00000047, 0x7AEFEDDC, 0x00000011, 0x7AF2CF90, 0x0000004F, 0x7BE0B8B0, 0x0000001B, 0x80EB3E88, 0x0000000A, 0x8213506A, 0x0000000C, 0x82468114, 0x00000011, 0x86B872A2, 0x0000001C, 0x87FBD296, 0x00000019, 0x88719339, 0x00000016, 0x89E2630A, 0x00000024, 0x8CB6536E, 0x0000004E, 0x92316E00, 0x00000015, 0x9415A51E, 0x0000004F, 0x94D658E0, 0x0000002B, 0x97E8DFCD, 0x00000036, 0x992E3874, 0x0000002A, 0x9B06958D, 0x00000030, 0x9B36B480, 0x0000000D, 0xA03CEFAD, 0x0000005A, 0xA39F47E6, 0x0000004E, 0xA946DEC4, 0x000000B4, 0xAE6173DC, 0x00000051, 0xB044A68D, 0x0000008C, 0xB29E36A8, 0x0000000B, 0xB82781F4, 0x0000000D, 0xC14DFAF8, 0x00000011, 0xC3F42E20, 0x0000001E, 0xC5E0065E, 0x00000067, 0xCAD68B21, 0x00000039, 0xCBF29AC7, 0x00000011, 0xCE8729BC, 0x0000001B, 0xD2A85A94, 0x00000004, 0xD34FA4F3, 0x00000011, 0xD64611B0, 0x00000058, 0xD814FD56, 0x00000018, 0xDD386A80, 0x0000000A, 0xDE82DFAC, 0x00000011, 0xEC68D16F, 0x0000001B, 0xEEDE845B, 0x0000003F, 0xF235F260, 0x0000008D, 0xF9AA1F0B, 0x00000087, 0xFC200887, 0x00000011, 0xFED657A3, 0x0000000C, 0x00000000]
奇数下标数据为key,偶数为基本块字节的长度。
用 python 实现基本块解密函数:
def fuck(prev_key, rip):
return rip ^ prev_key ^ ((rip * prev_key) & 0xffffffff) ^ (prev_key + rip)
def deXor(data, key):
key = p32(key)
data = bytearray(data)
for i in range(len(data)):
data[i] ^= key[i % 4]
return data
def decrypt_block(key, rip):
key2 = fuck(key, rip)
blockSize = get_size(key2)
if blockSize is None:
print("Not found1: rip:%x key:%x" % (rip, key2))
return None, None, None
offset = rip - 0x1000
code_data = code_bin[offset: offset + blockSize]
code_data = deXor(code_data, key)
next_rip = rip + blockSize - 2
key2 = fuck(key, next_rip)
jmps = get_jmps(key2)
return code_data, jmps, next_rip
4 • 控制流分析
ControlFlow1 回调,执行到无效指令会被调用,用于切换程序中的控制流。
该题用 3f 0f作为基本块的结尾(无效指令)触发 ControlFlow1 回调,切换控制流。
ControlFlow1 回调函数根据结尾rip与当前基本块的key计算另外一个 key,用于索引当前基本块的后继基本块的信息。
dd key
dd zf_0_jmp 当基本块结尾 zf 标志寄存器为 0 的跳转偏移,下面同理
dd zf_0_key
dd zf_1_jmp
dd zf_1_key
注意 v9 保存的是上一个基本块的 key,此处做的 += 运算,即上一个基本块的 key 与 下一个基本块的 key 有关联。
由此可见,基本块的 key 与控制流的路径有关!写解密脚本的时候要考虑路径问题。
另外,当虚拟机中程序运行到 0x10A3 时将调整控制流并更改 key:
解密后的基本块,很多都是以读取 fs 寄存器并判断结尾。
r15 的值来源于 fs:xxx ,最后再与 fs:xxx 内存的值比较,很明显最后的 zf = 1。
其实并不是,这道题对 fs 寄存器指向的那段内存做了内存读回调,回调如下:
每次读取 fs 指向的内存,该内存的值都会被改写。所以 mov 与 cmp 对 fs 内存访问出的结果是不同的,自然 zf = 0,走 zf = 0 的分支。
在控制流重建的时候,需要考虑以读取 fs 内存结尾的基本块,将其看作是无条件跳转,而不是 jz/jnz。
5 • 控制流重建
重建思路:
以 bfs 遍历顺序,从入口基本块开始解密,解密后再查询分支信息表获取后继基本块的相对偏移与key,最后将新基本块的信息加入到队列,等待分析。遍历时注意维护路径上的 key 累计值。
所有基本块解密完成后,可以得到每个基本块的后继基本块的相对偏移。
要在基本块的结尾插入跳转指令,这将改变代码布局,使得原始相对偏移不可用,所以我采取重编译来解决这个问题,重编译之前将原始基本块的入口地址作为基本的符号名,基本块结尾用 jmp/jz 等指令连接。
code.bin 文件时 dump 出来的原始 code 数据,输出 1.bin 可以直接在 ida 中反编译。
import ctypes
from capstone import *
from keystone import *
from pwn import *
context.arch = 'amd64'
from pwn import *
fuckTable = [0x00412F5E, 0xFFFFFA22, 0x14252652, 0xFFFFF9AC, 0x66CEF8EC, 0x0251D934, 0x0000009F, 0xC56FBF59, 0xFFFFFF61, 0xAA4D5B7C, 0x02745896, 0xFFFFFB7F, 0x34B6D31E, 0xFFFFFBB0, 0x302CC828, 0x02AC5992, 0xFFFFF524, 0x67CC4064, 0xFFFFF483, 0x8A5D9B26, 0x046254D0, 0xFFFFFC37, 0x074AB936, 0xFFFFFC7F, 0xB8EA37F7, 0x0CACD9FE, 0x0000007F, 0x6112F222, 0x00000002, 0x47A72561, 0x0F0FE6EB, 0xFFFFFBAE, 0x0A1411E7, 0xFFFFFC85, 0x3BE88B46, 0x0FC59DC2, 0xFFFFF72F, 0x7D12A5EF, 0xFFFFF691, 0xE67393D6, 0x10B1EBCA, 0x000001CF, 0x473A1295, 0x0000022E, 0x7BC15385, 0x1565D41D, 0xFFFFFDC4, 0x05D337BE, 0xFFFFFE7E, 0xE12982E4, 0x18909E40, 0x000005EB, 0xAE2337AF, 0x000005B1, 0x8E0AB2ED, 0x1AE7593A, 0xFFFFF3BC, 0x23E9058D, 0xFFFFF40B, 0xDFA6CF3E, 0x1B47DA81, 0xFFFFF8C3, 0x349CC616, 0xFFFFF7E9, 0x70C290D0, 0x1D816435, 0x00000002, 0x43F999C9, 0xFFFFFFD8, 0xAB0BCA16, 0x1DACC905, 0xFFFFFF54, 0x5C129962, 0xFFFFFD06, 0xE4515A41, 0x1E03B13C, 0xFFFFFF80, 0x7E763806, 0xFFFFF36A, 0xA25F3D93, 0x22FEFC06, 0xFFFFFCDD, 0xB94E0C2F, 0xFFFFFCB6, 0xF023033D, 0x26B1E690, 0xFFFFFDAB, 0xD0C7ED0C, 0xFFFFFE9B, 0xD49872C6, 0x2A652084, 0x000001EA, 0xDF9B65EE, 0x00000051, 0x5CC5AB90, 0x2FBEBD25, 0x0000048F, 0x60A4E9F2, 0x000009AF, 0x42FE8B0D, 0x34F12D90, 0x000004C0, 0xF6257D94, 0x00000480, 0x5227DE21, 0x35F591D0, 0xFFFFFCA1, 0xDA83E113, 0xFFFFF998, 0x805C7ECB, 0x37EB0B72, 0xFFFFF3EC, 0x7480201A, 0xFFFFF903, 0xAC977E11, 0x389A58A8, 0x00000189, 0xE4005CD7, 0xFFFFFDEC, 0xB043695F, 0x3CB24155, 0x0000084C, 0x8ACB6FF1, 0x00000899, 0xACB471A5, 0x3DCBCDE3, 0x000007A8, 0xA84E3072, 0x00000384, 0xB2624259, 0x3F5290DE, 0xFFFFFE25, 0x8AC11F92, 0xFFFFFD8A, 0x44ACCD78, 0x47FF9B7E, 0x00000A81, 0x9833BF9C, 0x00000B35, 0x9B7199CD, 0x4C7867E6, 0x0000011C, 0x68BB4F80, 0x0000002E, 0x75B675CD, 0x53ADCD80, 0x000004E8, 0x6AA4F705, 0x00000452, 0xBA7C314B, 0x566E1640, 0x00000C8E, 0x203E3737, 0x00000C38, 0xF9367ED9, 0x5EDBB130, 0x000004FF, 0xD4F71A40, 0x000002AA, 0x35DC4141, 0x6C29C83A, 0x00000013, 0xBEAD8A76, 0xFFFFFFB5, 0x7A8A43EF, 0x6E036C9C, 0x00000BD5, 0x225F81E0, 0x00000D89, 0x3C25944D, 0x6FDCCE50, 0x00000605, 0xD3126740, 0x000003D5, 0xA3DA544C, 0x7132D345, 0x0000064E, 0x00915A5A, 0x000006DD, 0x5BCB6B22, 0x720DBD5C, 0x000008C3, 0x64DCFDF6, 0x00000858, 0x190B20BB, 0x7A035AD4, 0x00000424, 0x4DD955FB, 0x000004BF, 0xF65150B5, 0x7CBAED22, 0x00000AA1, 0x62CC154B, 0xFFFFFC58, 0x8DD5CEDB, 0x7EBF8EA8, 0x00000458, 0xCE844A0E, 0xFFFFF734, 0x9079D6BA, 0x804885CD, 0x000007BB, 0x89A8DA66, 0x00000136, 0x7185B813, 0x82190F37, 0xFFFFF58C, 0x013FA7D4, 0xFFFFF4AB, 0x7518093D, 0x83F7826A, 0x00000917, 0x2F33C3DD, 0xFFFFFBF0, 0x02A289B1, 0x8481BFD5, 0xFFFFF927, 0x72EED2D1, 0xFFFFF80A, 0xF46FD351, 0x85A69D6E, 0x000000B4, 0x27A3BB0F, 0x00000181, 0x49235BC0, 0x85F73150, 0x00000259, 0xA300692F, 0x000009BD, 0x5A3E46A9, 0x86E2497A, 0xFFFFFB53, 0xE7614707, 0xFFFFFBB3, 0xFA190B2A, 0x8B261F60, 0xFFFFF323, 0x97B9CC33, 0xFFFFFAB7, 0x2CB73BF0, 0x8B42B00C, 0x00000871, 0xA57A2DE3, 0x00000797, 0xA73082D6, 0x8E4C5C94, 0x000000FE, 0xEE4B594B, 0xFFFFF999, 0xDCE3B74D, 0x913A9FDB, 0xFFFFFE1C, 0x1BFFA329, 0xFFFFFD31, 0x49B21C95, 0x922BFB96, 0xFFFFF61B, 0x4FAFD829, 0xFFFFFBBA, 0x6BD5D317, 0x9F4B8702, 0xFFFFFEC1, 0xB691AD49, 0xFFFFFEF2, 0xCE6C6FE9, 0xA2CEAAA6, 0xFFFFFD89, 0x60E52701, 0xFFFFFCB2, 0x25AD9A9D, 0xAA970D72, 0xFFFFF2BB, 0xC1F58CAC, 0xFFFFF2AB, 0x20B8FE22, 0xABC02B72, 0xFFFFF94B, 0xFF6EA5A6, 0xFFFFFA6A, 0x1CD46647, 0xAE535E9E, 0x000003EC, 0x31246F6B, 0x0000035B, 0x50E2A20A, 0xB7337941, 0xFFFFF856, 0xD1A79AD7, 0xFFFFF955, 0x14673B75, 0xBB8DB95E, 0xFFFFFEEB, 0x6A7F1E5A, 0xFFFFF3B3, 0x1EF2F3AA, 0xBC1EDA22, 0xFFFFFB90, 0xE247955F, 0xFFFFFCE6, 0xA0351A85, 0xBCD91FE8, 0x0000008C, 0x71A348B9, 0x00000030, 0x821754EF, 0xBD38E305, 0xFFFFFF59, 0xE694333F, 0xFFFFFEF9, 0x436B1A45, 0xBE1AA65A, 0xFFFFF93D, 0x8761A810, 0xFFFFFEEB, 0xB2DB19FA, 0xC052453C, 0x000009B5, 0xB05027D7, 0x000009C5, 0xBCA91679, 0xC4A2D780, 0x000008B9, 0xE42FD068, 0x000007C1, 0x9F8B2B83, 0xC6A236BA, 0xFFFFFDBB, 0x20649A12, 0xFFFFFD09, 0x5F73FD94, 0xC6BB5160, 0xFFFFFE90, 0x0ED42674, 0xFFFFFF4B, 0xA76699CC, 0xCB74E940, 0x000003E3, 0x7DA194FF, 0xFFFFFCDA, 0xB23E5B15, 0xD027B387, 0xFFFFF701, 0x880BCC4F, 0xFFFFF785, 0xFEA3D685, 0xD1127D6B, 0xFFFFFAC0, 0xDF3D499A, 0x00000362, 0x84B7777D, 0xD6F5F913, 0xFFFFFCCD, 0xA5D89DB8, 0xFFFFFCAB, 0xF69BAE29, 0xDD04F828, 0xFFFFF705, 0xE18F3BA0, 0xFFFFF64D, 0xEBC799B0, 0xDDF22CB8, 0x0000075D, 0x47F7B857, 0x000001B3, 0x5C1CDEA9, 0xDF34D0A8, 0x0000014D, 0xBFE2CAD5, 0x00000201, 0x1F0C8A89, 0xE146EA40, 0x0000046D, 0x189EB8F9, 0xFFFFF6FB, 0x4CA1090D, 0xE231C560, 0x00000710, 0x2E586529, 0xFFFFFF17, 0x0E9AA776, 0xE2FC6838, 0x00000733, 0xB73DDD7A, 0x00000753, 0x14A1BDE4, 0xE44AE35D, 0x000002C8, 0x46B1F3D1, 0xFFFFFA2D, 0xD2295816, 0xE5AF4AB1, 0x00000DB0, 0x0AFF4FF9, 0x00000D91, 0xB17A4340, 0xE7E3CF21, 0x00000656, 0x9FC50924, 0x00000658, 0x31615022, 0xE8815965, 0x00000BCB, 0x6F51A655, 0x00000C0A, 0x72F5680C, 0xEBDF0F14, 0xFFFFF2B1, 0xD36EC5D4, 0xFFFFF239, 0x3B711343, 0xEC12E59B, 0x00000270, 0x3A38D2E8, 0x0000023D, 0x68D07674, 0xF4013920, 0x00000703, 0xD83CCFAA, 0x000007BA, 0x46891EEB, 0xF6847EC1, 0xFFFFFE62, 0x6D4BAAFC, 0xFFFFFD92, 0x5E6F5A94]
size_table = [0x02F73020, 0x00000015, 0x09D3473A, 0x00000051, 0x0EF87B55, 0x0000000D, 0x147CB028, 0x00000023, 0x15F833AA, 0x00000030, 0x17086780, 0x00000018, 0x1733A9D4, 0x00000014, 0x17D61EE8, 0x00000051, 0x1D52F19E, 0x00000011, 0x1F732DE0, 0x0000000D, 0x1FBECFAD, 0x0000001B, 0x245BD7C8, 0x00000055, 0x25E7ABEE, 0x00000009, 0x2882C190, 0x000000A2, 0x2A2084A0, 0x00000075, 0x326AA6AE, 0x00000036, 0x33074A36, 0x00000024, 0x3440BD69, 0x0000002C, 0x362A1FC3, 0x0000002C, 0x3C0450D0, 0x0000000D, 0x3CB575FD, 0x00000011, 0x41B3B26E, 0x0000004E, 0x46005120, 0x00000011, 0x465A72CF, 0x00000002, 0x492145A0, 0x0000000D, 0x49AA4CE0, 0x0000002D, 0x4BD63647, 0x0000004E, 0x4BF84A87, 0x0000000D, 0x4D102445, 0x00000033, 0x4D4D3C55, 0x0000001B, 0x53723232, 0x0000000A, 0x5809B5CB, 0x000000A2, 0x5B12FFCE, 0x00000015, 0x5B1F3000, 0x00000051, 0x5D9FBD20, 0x00000027, 0x6219EED9, 0x0000008A, 0x65D82D17, 0x0000004C, 0x67F5671A, 0x00000063, 0x6CE2CBC1, 0x00000033, 0x718A739C, 0x0000000B, 0x71A62DD7, 0x00000015, 0x7693A1F6, 0x00000014, 0x7A473FB0, 0x00000047, 0x7AEFEDDC, 0x00000011, 0x7AF2CF90, 0x0000004F, 0x7BE0B8B0, 0x0000001B, 0x80EB3E88, 0x0000000A, 0x8213506A, 0x0000000C, 0x82468114, 0x00000011, 0x86B872A2, 0x0000001C, 0x87FBD296, 0x00000019, 0x88719339, 0x00000016, 0x89E2630A, 0x00000024, 0x8CB6536E, 0x0000004E, 0x92316E00, 0x00000015, 0x9415A51E, 0x0000004F, 0x94D658E0, 0x0000002B, 0x97E8DFCD, 0x00000036, 0x992E3874, 0x0000002A, 0x9B06958D, 0x00000030, 0x9B36B480, 0x0000000D, 0xA03CEFAD, 0x0000005A, 0xA39F47E6, 0x0000004E, 0xA946DEC4, 0x000000B4, 0xAE6173DC, 0x00000051, 0xB044A68D, 0x0000008C, 0xB29E36A8, 0x0000000B, 0xB82781F4, 0x0000000D, 0xC14DFAF8, 0x00000011, 0xC3F42E20, 0x0000001E, 0xC5E0065E, 0x00000067, 0xCAD68B21, 0x00000039, 0xCBF29AC7, 0x00000011, 0xCE8729BC, 0x0000001B, 0xD2A85A94, 0x00000004, 0xD34FA4F3, 0x00000011, 0xD64611B0, 0x00000058, 0xD814FD56, 0x00000018, 0xDD386A80, 0x0000000A, 0xDE82DFAC, 0x00000011, 0xEC68D16F, 0x0000001B, 0xEEDE845B, 0x0000003F, 0xF235F260, 0x0000008D, 0xF9AA1F0B, 0x00000087, 0xFC200887, 0x00000011, 0xFED657A3, 0x0000000C, 0x00000000]
zf_0_jmp = 0
zf_0_key = 1
zf_1_jmp = 2
zf_1_key = 3
code_bin = open("code.bin", "rb").read()
print("code size: ", hex(len(code_bin)))
def get_size(key):
for i in range(85):
if size_table[i * 2] == key:
return size_table[i * 2 + 1]
return None
def get_jmps(key):
for i in range(85):
base = i * 5
if fuckTable[base] == key:
# zf_0_jmp, zf_0_key, zf_1_jmp, zf_1_key
return fuckTable[base + 1: base + 5]
print("not found2: ", hex(key))
return None
def fuck(prev_key, rip):
return rip ^ prev_key ^ ((rip * prev_key) & 0xffffffff) ^ (prev_key + rip)
def deXor(data, key):
key = p32(key)
data = bytearray(data)
for i in range(len(data)):
data[i] ^= key[i % 4]
return data
def decrypt_block(key, rip):
key2 = fuck(key, rip)
blockSize = get_size(key2)
if blockSize is None:
print("Not found1: rip:%x key:%x" % (rip, key2))
return None, None, None
offset = rip - 0x1000
code_data = code_bin[offset: offset + blockSize]
code_data = deXor(code_data, key)
next_rip = rip + blockSize - 2
key2 = fuck(key, next_rip)
jmps = get_jmps(key2)
return code_data, jmps, next_rip
class Node:
def __init__(self, data, rip):
self.code_data = data
self.child1 = 0
self.child2 = 0
self.end_rip = None
self.rip = rip
def disasm(data, baseaddr):
md = Cs(CS_ARCH_X86, CS_MODE_64)
ins = ''
for i in md.disasm(data, baseaddr):
asm_code = "%s\t%s" % (i.mnemonic, i.op_str)
ins += asm_code + "\n"
return ins
def buildNode(key_, rip_):
work_queue = [(key_, rip_)]
log_map = {}
while len(work_queue) > 0:
T = work_queue[0]
work_queue.remove(T)
key, rip = T
if rip in log_map:
continue
if rip == 0x10A3:
key -= 0x2B09B990
rip = 0x1EEC
code_data, jmps, next_rip = decrypt_block(key, rip)
if code_data is None:
continue
node_cur = Node(code_data, rip)
node_cur.end_rip = next_rip
log_map[rip] = node_cur
if jmps is None:
continue
asm_text = disasm(node_cur.code_data, rip)
newrip = next_rip + ctypes.c_int32(jmps[zf_0_jmp]).value
node_cur.child1 = newrip
if newrip not in log_map:
work_queue.append(((key + jmps[zf_0_key]) & 0xffffffff, newrip))
if 'qword ptr fs:[' in asm_text.splitlines()[-1]:
continue
newrip = next_rip + ctypes.c_int32(jmps[zf_1_jmp]).value
node_cur.child2 = newrip
if newrip not in log_map:
work_queue.append(((key + jmps[zf_1_key]) & 0xffffffff, newrip))
jmptables = {}
for i in sorted(log_map.keys()):
if log_map[i].child1 is None:
log_map[i].child1 = 0
if log_map[i].child2 is None:
log_map[i].child2 = 0
jmptables[hex(log_map[i].end_rip)] = (hex(log_map[i].rip), hex(log_map[i].child1), hex(log_map[i].child2))
print("len:", len(jmptables))
print(jmptables)
all_asm = ''
for i in sorted(log_map.keys()):
print(hex(i))
node = log_map[i]
if i == 0x1EEC:
all_asm += "_0x10a3:\n"
all_asm += "_" + hex(i) + ":\n"
all_asm += disasm(node.code_data, i)
if node.child1 != 0 and node.child2 != 0:
jmp_code = "jz _" + hex(node.child2) + "\n"
jmp_code += "jmp _" + hex(node.child1) + "\n"
elif node.child1 == 0 and node.child2 != 0:
jmp_code = "jmp _" + hex(node.child2) + "\n"
elif node.child2 == 0 and node.child1 != 0:
jmp_code = "jmp _" + hex(node.child1) + "\n"
else:
jmp_code = '\n'
all_asm += jmp_code
all_asm = all_asm.replace("endbr64", "nop\n" * 4)
code_bin = asm(all_asm)
open('1.bin', 'wb').write(code_bin)
print(all_asm)
print("all nodes: ", len(log_map))
buildNode(0x3265B1F5, 0x1000)
6 • 提取出来的代码分析
// rsp = 0x7777F000
void __noreturn sub_4()
{
unsigned __int8 v0; // al
int v1; // eax
__int64 v2; // rax
__int64 xorKey[4]; // [rsp+0h] [rbp-2C0h]
__int128 v4; // [rsp+21h] [rbp-29Fh] BYREF
char v5[14]; // [rsp+31h] [rbp-28Fh] BYREF
char v6[17]; // [rsp+3Fh] [rbp-281h] BYREF
__int64 compare[5]; // [rsp+50h] [rbp-270h] BYREF
__int64 v8; // [rsp+78h] [rbp-248h]
__int64 FlagInData[8]; // [rsp+80h] [rbp-240h] BYREF
unsigned __int8 v10; // [rsp+C2h] [rbp-1FEh]
unsigned __int8 v11; // [rsp+C3h] [rbp-1FDh]
int kk; // [rsp+C4h] [rbp-1FCh]
__int64 v13; // [rsp+C8h] [rbp-1F8h]
__int128 *v14; // [rsp+D0h] [rbp-1F0h]
unsigned __int8 v15; // [rsp+DAh] [rbp-1E6h]
unsigned __int8 v16; // [rsp+DBh] [rbp-1E5h]
int mm; // [rsp+DCh] [rbp-1E4h]
__int64 v18; // [rsp+E0h] [rbp-1E0h]
__int64 v19; // [rsp+E8h] [rbp-1D8h]
unsigned __int8 v20; // [rsp+F3h] [rbp-1CDh]
int jj; // [rsp+F4h] [rbp-1CCh]
__int64 v22; // [rsp+F8h] [rbp-1C8h]
__int64 *v23; // [rsp+100h] [rbp-1C0h]
__int64 *v24; // [rsp+108h] [rbp-1B8h]
int ii; // [rsp+110h] [rbp-1B0h]
unsigned int v26; // [rsp+114h] [rbp-1ACh]
int v27; // [rsp+118h] [rbp-1A8h]
unsigned int v28; // [rsp+11Ch] [rbp-1A4h]
unsigned int *v29; // [rsp+120h] [rbp-1A0h]
int n; // [rsp+12Ch] [rbp-194h]
unsigned __int64 v31; // [rsp+130h] [rbp-190h]
unsigned __int64 v32; // [rsp+138h] [rbp-188h]
unsigned __int64 v33; // [rsp+140h] [rbp-180h]
unsigned __int64 data2; // [rsp+148h] [rbp-178h]
int m; // [rsp+154h] [rbp-16Ch]
unsigned __int64 v36; // [rsp+158h] [rbp-168h]
unsigned __int64 data1; // [rsp+160h] [rbp-160h]
int i_0; // [rsp+16Ch] [rbp-154h]
__int64 v39; // [rsp+170h] [rbp-150h]
unsigned __int64 from_t0; // [rsp+178h] [rbp-148h]
__int64 const_32; // [rsp+180h] [rbp-140h]
__int64 *flag_ptr; // [rsp+188h] [rbp-138h]
unsigned __int64 v43; // [rsp+190h] [rbp-130h]
__int64 v44; // [rsp+198h] [rbp-128h]
__int64 *v45; // [rsp+1A0h] [rbp-120h]
unsigned __int8 v46; // [rsp+202h] [rbp-BEh]
unsigned __int8 v47; // [rsp+203h] [rbp-BDh]
int nn; // [rsp+204h] [rbp-BCh]
__int64 v49; // [rsp+208h] [rbp-B8h]
char *v50; // [rsp+210h] [rbp-B0h]
__int64 flagLen; // [rsp+218h] [rbp-A8h]
__int64 *v52; // [rsp+220h] [rbp-A0h]
unsigned __int8 v53; // [rsp+22Ah] [rbp-96h]
unsigned __int8 v54; // [rsp+22Bh] [rbp-95h]
int i1; // [rsp+22Ch] [rbp-94h]
__int64 v56; // [rsp+230h] [rbp-90h]
char *v57; // [rsp+238h] [rbp-88h]
__int64 v58; // [rsp+240h] [rbp-80h]
__int64 time0; // [rsp+248h] [rbp-78h]
__int64 v60; // [rsp+250h] [rbp-70h]
__int64 d2; // [rsp+258h] [rbp-68h]
__int64 v62; // [rsp+260h] [rbp-60h]
__int64 d1; // [rsp+268h] [rbp-58h]
int i; // [rsp+274h] [rbp-4Ch]
__int64 data0; // [rsp+278h] [rbp-48h]
unsigned __int8 v66; // [rsp+282h] [rbp-3Eh]
unsigned __int8 v67; // [rsp+283h] [rbp-3Dh]
int j; // [rsp+284h] [rbp-3Ch]
__int64 v69; // [rsp+288h] [rbp-38h]
char *v70; // [rsp+290h] [rbp-30h]
unsigned __int8 v71; // [rsp+29Ah] [rbp-26h]
unsigned __int8 v72; // [rsp+29Bh] [rbp-25h]
int k; // [rsp+29Ch] [rbp-24h]
__int64 v74; // [rsp+2A0h] [rbp-20h]
__int64 *flagIn; // [rsp+2A8h] [rbp-18h]
__int64 v76; // [rsp+2B0h] [rbp-10h]
unsigned __int64 time0_1; // [rsp+2B8h] [rbp-8h]
v58 = 0x7177625F32303231i64;
__writefsqword(0, 0x7177625F32303231ui64);
__asm { syscall; Low latency system call }
time0 = MEMORY[0x1337]; // time(0)
time0_1 = MEMORY[0x1337];
v2 = MEMORY[0x1337] / 0xE10ui64;
data0 = MEMORY[0x1337] / 0xE10ui64;
for ( i = 0; i != 256; ++i )
{
d1 = 0i64;
d1 = *(_QWORD *)v2;
v62 = d1;
d2 = 0i64;
d2 = *(_QWORD *)(d1 + 1);
v60 = d2;
data0 = __ROL8__((data0 ^ d1) + d2 + 33 * data0 + 1, 13);
if ( (i & 1) != 0 )
data0 = v60 ^ (v62 + data0);
if ( (i & 2) != 0 )
data0 ^= v62 + v60;
if ( (i & 4) != 0 )
data0 ^= v60 ^ v62;
v2 = i & 8;
if ( (i & 8) != 0 )
{
v2 = data0 + v62 + v60;
data0 = v2;
}
}
v76 = data0;
strcpy(v6, "input flag:\n");
v70 = v6;
v69 = 12i64;
for ( j = 0; v69 != j; ++j )
{
v67 = v70[j];
v66 = v67;
__outbyte(1u, v67); // putchar
}
memset(FlagInData, 0, sizeof(FlagInData));
flagIn = FlagInData;
v74 = 0x40i64;
for ( k = 0; ; ++k )
{
if ( v74 == k )
goto LABEL_3;
v0 = __inbyte(0); // get_char
v72 = v0;
v71 = v0;
if ( v0 == '\n' )
break;
*((_BYTE *)flagIn + k) = v71;
}
*((_BYTE *)flagIn + k) = 0;
LABEL_3:
if ( v76 == 0x1C986C3B22EA63E5i64 )
{
v52 = FlagInData;
for ( flagLen = 0i64; *((_BYTE *)v52 + flagLen); ++flagLen )
;
v8 = flagLen;
if ( flagLen == 32 )
{
v45 = FlagInData;
v44 = 32i64;
v43 = time0_1 / 0xE10;
flag_ptr = FlagInData;
const_32 = 32i64;
from_t0 = time0_1 / 0xE10;
v39 = 0x5249415452455451i64;
__writefsqword(0, 0x5249415452455451ui64);
for ( i_0 = 0; const_32 != i_0; ++i_0 )
{
data1 = __readfsqword(0); // 0x5249415452455451
v36 = i_0;
for ( m = 0; m != 256; ++m )
{
data2 = __readfsqword(0);
v33 = data2;
v32 = data2;
v31 = data2;
v36 = (v36 ^ data2) + data2 + 0x21 * v36 + 1;
v36 = __ROL8__(v36, 13);
if ( (m & 1) != 0 )
v36 = v31 ^ (v33 + v36);
if ( (m & 2) != 0 )
v36 ^= v33 + v31;
if ( (m & 4) != 0 )
v36 ^= v31 ^ v33;
if ( (m & 8) != 0 )
v36 += v33 + v31;
}
*((_BYTE *)flag_ptr + i_0) ^= (_BYTE)data1 + (_BYTE)v36;
}
for ( n = 0; const_32 != n; n += 4 ) // 4字节一组
{
v29 = (unsigned int *)((char *)flag_ptr + n);
v28 = *v29;
v27 = 0;
v26 = v28;
v28 = from_t0 + _mm_crc32_u32(0, v28);
*v29 = v28;
}
xorKey[0] = 0x178DEC4F232DDB6Ei64;
xorKey[1] = 0xC2AAB7D6D2A167C3ui64;
xorKey[2] = 0xF1AB91F72761A80Fui64;
xorKey[3] = 0x3DCEDC28076C41Ai64;
for ( ii = 0; v44 != ii; ++ii )
*((_BYTE *)v45 + ii) ^= *((_BYTE *)xorKey + ii);
compare[0] = 0x3EC81D9432CEF584i64;
compare[1] = 0xB649A4DCD6BD24FEui64;
compare[2] = 0xC5927F0B767A787Dui64;
compare[3] = 0x1F245B7F751BB52Ei64;
v24 = FlagInData;
v23 = compare;
v22 = v8;
for ( jj = 0; ; ++jj )
{
if ( v22 == jj )
{
v1 = 0;
goto LABEL_47;
}
v20 = *((_BYTE *)v24 + jj) - *((_BYTE *)v23 + jj);
if ( v20 )
break;
}
v1 = v20;
LABEL_47:
if ( v1 )
{
strcpy((char *)&v4, "wrong\n");
v14 = &v4;
v13 = 6i64;
for ( kk = 0; v13 != kk; ++kk )
{
v11 = *((_BYTE *)v14 + kk);
v10 = v11;
__outbyte(1u, v11);
}
}
else
{
strcpy((char *)&v4 + 7, "correct\n");
v19 = (__int64)&v4 + 7;
v18 = 8i64;
for ( mm = 0; v18 != mm; ++mm )
{
v16 = *(_BYTE *)(mm + v19);
v15 = v16;
__outbyte(1u, v16);
}
}
}
else
{
strcpy(v5, "wrong\n");
v50 = v5;
v49 = 6i64;
for ( nn = 0; v49 != nn; ++nn )
{
v47 = v50[nn];
v46 = v47;
__outbyte(1u, v47);
}
}
}
else
{
*(_DWORD *)&v5[7] = 'norw';
*(_WORD *)&v5[11] = '\ng';
v5[13] = 0;
v57 = &v5[7];
v56 = 6i64;
for ( i1 = 0; v56 != i1; ++i1 )
{
v54 = v57[i1];
v53 = v54;
__outbyte(1u, v54);
}
}
__halt();
}
为了屏蔽IDA的优化,方便分析,我做了一些小 patch:
syscall 的 unicorn 回调会修改 rax 寄存器,但是 ida 认为 rax 的值是一个可计算的常数,于是后面与 rax 有关的表达式都被优化。
for 循环中读取了两次 fs:0 , 有unicorn回调,每次读取的值肯定不一样,ida 认为两次读取的值一样,于是优化成一次访问。
手动 patch ,即可解决这些问题。
有两种爆破思路:
直接爆破 time(0)
根据 flag 信息反向爆破
可以用 flag 开头来爆破 time(0) 时间常数。
from_t0 = time_0 / 0xE10;
由于 4字节一组,qwb{与QWB{ 与 flag 与 FLAG 都是已知的 4 个字节并且 v28 可以非常轻松的获得。
通过爆破开头也可以反推 time(0)。
爆破出 time(0) 后的做法就是非常简单的计算任务了。
#include <iostream>
#include "ida.h"
#include <x86intrin.h>
static uint64 fs_0;
uint64 getFS0() {
// v7[0] = 8461816625668189699LL * v7[0] + 841540768324766462LL;
fs_0 = 0x756E69636F726E03 * fs_0 + 0xBADC0DEC001CAFE;
return fs_0;
}
void setFSO(int64_t val) {
fs_0 = val;
}
uint64 test_time(uint64 time) {
uint64 v35;
setFSO(0x7177625F32303231);
uint64 v2 = time ;
uint64 data0 = time ;
uint64 d1, d2,v62, v60;
for (int i = 0; i != 256; ++i )
{
d1 = 0;
d1 = getFS0();
v62 = d1;
d2 = getFS0();
v60 = d2;
data0 = __ROL8__((data0 ^ d1) + d2 + 33 * data0 + 1, 13);
if ( (i & 1) != 0 )
data0 = v60 ^ (v62 + data0);
if ( (i & 2) != 0 )
data0 ^= v62 + v60;
if ( (i & 4) != 0 )
data0 ^= v60 ^ v62;
v2 = i & 8;
if ( (i & 8) != 0 )
{
v2 = data0 + v62 + v60;
data0 = v2;
}
}
printf("%p ==\n", data0);
return data0;
}
int main() {
std::cout << "Hello, World!" << std::endl;
unsigned __int64 compare[4];
unsigned __int64 xorKey[4];
unsigned int flags[] = {0x67616c66, 0x47414c46, 0x7b627771, 0x7b425751};
unsigned char xorkey0[32] = {0};
uint32 * xorkey0_4 = (uint32 *)xorkey0;
compare[0] = 0x3EC81D9432CEF584; // flag, FLAG, qwb{ ,QWB{
compare[1] = 0xB649A4DCD6BD24FE;
compare[2] = 0xC5927F0B767A787D;
compare[3] = 0x1F245B7F751BB52E;
xorKey[0] = 0x178DEC4F232DDB6E;
xorKey[1] = 0xC2AAB7D6D2A167C3;
xorKey[2] = 0xF1AB91F72761A80F;
xorKey[3] = 0x3DCEDC28076C41A;
unsigned int * compare_4 = (unsigned int *)compare;
unsigned int fs;
uint64 v35, v36, v33,v32, v31, v30;
setFSO(0x5249415452455451);
for (int i_0 = 0; 32 != i_0; ++i_0 )
{
uint64 data0 = getFS0(); // 0x5249415452455451
v35 = i_0;
for (int m = 0; m != 256; ++m )
{
uint64 data1 = getFS0();
uint64 data2 = getFS0();
v35 = (v35 ^ data1) + data2 + 0x21 * v35 + 1;
v35 = __ROL8__(v35, 13);
if ( (m & 1) != 0 )
v35 = data2 ^ (data1 + v35);
if ( (m & 2) != 0 )
v35 ^= data2 + data1;
if ( (m & 4) != 0 )
v35 ^= data2 ^ data1;
if ( (m & 8) != 0 )
v35 += data2 + data1;
}
xorkey0[i_0] = (_BYTE)data0 + (_BYTE)v35;
}
for(int k = 0; k < 4; k++) {
unsigned char * data = (unsigned char *)&flags[k];
for (int i = 0 ; i < 4; i++) {
data[i] ^= xorkey0[i];
}
// printf("%x\n", flags[k]);
}
for (int ii = 0; 32 != ii; ++ii )
*((_BYTE *)compare + ii) ^= *((_BYTE *)xorKey + ii);
uint64 targetTT = 0;
for (int i = 0; i < 4; i++)
{
// v27 = from_t0 + _mm_crc32_u32(0, v27);
// unsigned int from_t0 = compare_4[0] - _mm_crc32_u32(0, flags[i]);
uint64 tt = compare_4[0] - _mm_crc32_u32(0, flags[i]);
uint64 rr = test_time(tt);
if (rr == 0x1c986c3b22ea63e5) {
targetTT = tt;
printf("val:%p\n", tt);
}
}
uint32 flag_arr[8];
for (int i = 0; i < 8; ++i) { // 4 * 8
for (uint32 val = 0; val != 0xffffffff; ++val) {
if (compare_4[i] - targetTT == _mm_crc32_u32(0, val)) {
flag_arr[i] = val;
break;
}
}
}
for (int i = 0; i < 8; ++i) {
flag_arr[i] ^= xorkey0_4[i];
}
printf("%s", (char *)flag_arr);
return 0;
}
看雪ID:无名侠
https://bbs.pediy.com/user-home-617255.htm
*本文由看雪论坛 无名侠 原创,转载请注明来自看雪社区
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!