# hitcon2023 逆向 The Blade WP
运行所给的程序 socket 通信相关
由题目描述 A Rust tool for executing shellcode in a seccomp environment.
字符串查找定位 发现其中 verify 方法可疑
发现隐藏了一个命令 flag 是以添加命令行参数的方式输入 flag 调试得知输入 flag 长度为 64 位
F7 进入 verify 发现加密逻辑 简单来讲 是一个大的 256 次 while 循环 里面有好几个通过固定的表对输入进行打乱,然后一个有点复杂的加密逻辑,有取余有除有异或,于是就有个骚操作的想法,既然逆向加密算法比较复杂,就找出 00-ff 的映射就行了 因为是按位加密的伐然后前面的几次打乱顺序由于是固定的表所以可以看成一次打乱顺序 找出索引交换的顺序即可
do | |
{ | |
++v8; | |
memcpy(dest, "/", 0x200uLL); | |
v9 = 64LL; | |
v10 = &dest[1]; | |
do | |
{ | |
v11 = *(v10 - 1); | |
if ( v11 >= 64 | |
|| (v12 = flagg[v9 - 1], flagg[v9 - 1] = flagg[v11], flagg[v11] = v12, v13 = *v10, (unsigned __int64)*v10 > 0x3F) ) | |
{ | |
LABEL_53: | |
core::panicking::panic_bounds_check::h7d0e683548e4cb10(); | |
} | |
v14 = flagg[v9 - 2]; | |
flagg[v9 - 2] = flagg[v13]; | |
flagg[v13] = v14; | |
v10 += 2; | |
v9 -= 2LL; | |
} | |
while ( v9 ); | |
memcpy(dest, &unk_55C3745E9D20, 0x200uLL); | |
v15 = 64LL; | |
v16 = &dest[1]; | |
do | |
{ | |
v17 = *(v16 - 1); | |
if ( v17 > 63 ) | |
goto LABEL_53; | |
v18 = flagg[v15 - 1]; | |
flagg[v15 - 1] = flagg[v17]; | |
flagg[v17] = v18; | |
v19 = *v16; | |
if ( (unsigned __int64)*v16 > 63 ) | |
goto LABEL_53; | |
v20 = flagg[v15 - 2]; | |
flagg[v15 - 2] = flagg[v19]; | |
flagg[v19] = v20; | |
v16 += 2; | |
v15 -= 2LL; | |
} | |
while ( v15 ); | |
memcpy(dest, &unk_55C3745E9F20, 0x200uLL); | |
v21 = 64LL; | |
v22 = &dest[1]; | |
do | |
{ | |
v23 = *(v22 - 1); | |
if ( v23 > 0x3F ) | |
goto LABEL_53; | |
v24 = flagg[v21 - 1]; | |
flagg[v21 - 1] = flagg[v23]; | |
flagg[v23] = v24; | |
v25 = *v22; | |
if ( (unsigned __int64)*v22 > 0x3F ) | |
goto LABEL_53; | |
v26 = flagg[v21 - 2]; | |
flagg[v21 - 2] = flagg[v25]; | |
flagg[v25] = v26; | |
v22 += 2; | |
v21 -= 2LL; | |
} | |
while ( v21 ); | |
memcpy(dest, &unk_55C3745EA120, 0x200uLL); | |
v27 = 64LL; | |
v28 = &dest[1]; | |
do | |
{ | |
v29 = *(v28 - 1); | |
if ( v29 > 0x3F ) | |
goto LABEL_53; | |
v30 = flagg[v27 - 1]; | |
flagg[v27 - 1] = flagg[v29]; | |
flagg[v29] = v30; | |
v31 = *v28; | |
if ( (unsigned __int64)*v28 > 0x3F ) | |
goto LABEL_53; | |
v32 = flagg[v27 - 2]; | |
flagg[v27 - 2] = flagg[v31]; | |
flagg[v31] = v32; | |
v28 += 2; | |
v27 -= 2LL; | |
} | |
while ( v27 ); | |
memcpy(dest, &unk_55C3745EA320, 0x200uLL); | |
v33 = 64LL; | |
v34 = &dest[1]; | |
do | |
{ | |
v35 = *(v34 - 1); | |
if ( v35 > 0x3F ) | |
goto LABEL_53; | |
v36 = flagg[v33 - 1]; | |
flagg[v33 - 1] = flagg[v35]; | |
flagg[v35] = v36; | |
v37 = *v34; | |
if ( (unsigned __int64)*v34 > 0x3F ) | |
goto LABEL_53; | |
v38 = flagg[v33 - 2]; | |
flagg[v33 - 2] = flagg[v37]; | |
flagg[v37] = v38; | |
v34 += 2; | |
v33 -= 2LL; | |
} | |
while ( v33 ); | |
memcpy(dest, &unk_55C3745EA520, 0x200uLL); | |
v39 = 64LL; | |
v40 = &dest[1]; | |
do | |
{ | |
v41 = *(v40 - 1); | |
if ( v41 > 0x3F ) | |
goto LABEL_53; | |
v42 = flagg[v39 - 1]; | |
flagg[v39 - 1] = flagg[v41]; | |
flagg[v41] = v42; | |
v43 = *v40; | |
if ( (unsigned __int64)*v40 > 0x3F ) | |
goto LABEL_53; | |
v44 = flagg[v39 - 2]; | |
flagg[v39 - 2] = flagg[v43]; | |
flagg[v43] = v44; | |
v40 += 2; | |
v39 -= 2LL; | |
} | |
while ( v39 ); | |
memcpy(dest, &unk_55C3745EA720, 0x200uLL); | |
v45 = 64LL; | |
v46 = &dest[1]; | |
do | |
{ | |
v47 = *(v46 - 1); | |
if ( v47 > 0x3F ) | |
goto LABEL_53; | |
v48 = flagg[v45 - 1]; | |
flagg[v45 - 1] = flagg[v47]; | |
flagg[v47] = v48; | |
v49 = *v46; | |
if ( (unsigned __int64)*v46 > 0x3F ) | |
goto LABEL_53; | |
v50 = flagg[v45 - 2]; | |
flagg[v45 - 2] = flagg[v49]; | |
flagg[v49] = v50; | |
v46 += 2; | |
v45 -= 2LL; | |
} | |
while ( v45 ); | |
memcpy(dest, &unk_55C3745EA920, 0x200uLL); | |
v52 = 64LL; | |
v53 = &dest[1]; | |
do | |
{ | |
v54 = *(v53 - 1); | |
if ( v54 > 0x3F ) | |
goto LABEL_53; | |
v55 = flagg[v52 - 1]; | |
flagg[v52 - 1] = flagg[v54]; | |
flagg[v54] = v55; | |
v56 = *v53; | |
if ( (unsigned __int64)*v53 > 0x3F ) | |
goto LABEL_53; | |
v57 = flagg[v52 - 2]; | |
flagg[v52 - 2] = flagg[v56]; | |
flagg[v56] = v57; | |
v53 += 2; | |
v52 -= 2LL; | |
} | |
while ( v52 ); | |
v58 = 0LL; | |
do | |
{ | |
v59 = (unsigned __int8)flagg[v58] + 1; | |
LOWORD(v51) = 1; | |
LOWORD(v52) = 257; | |
v60 = 0; | |
do | |
{ | |
v62 = v52; | |
LOWORD(v52) = (unsigned __int16)v52 / (unsigned __int16)v59; | |
v61 = v62 % (unsigned __int16)v59; | |
v63 = v51; | |
v51 = v60 - v51 * v52; | |
LODWORD(v52) = v59; | |
v59 = (unsigned __int16)(v62 % (unsigned __int16)v59); | |
v60 = v63; | |
} | |
while ( v61 ); | |
v64 = 0; | |
if ( (__int16)v63 > 0 ) | |
v64 = v63; | |
flagg[v58] = ((unsigned __int16)(v64 + ((__int16)v63 >> 15) - v63) / 0x101u | |
+ v63 | |
+ ((unsigned __int16)v63 >> 15) | |
+ 113) ^ 0x89; | |
v52 = v58 + 1; | |
v58 = v52; | |
} | |
while ( v52 != 64 ); | |
} | |
while ( v8 != 256 );// 提取出的总的加密 |
不出意外的话 64 位比较数据是 cmp=[0xA7, 0x51, 0x68, 0x52, 0x85, 0x27, 0xFF, 0x31, 0x88, 0x87, 0xD2, 0xC7, 0xD3, 0x23, 0x3F, 0x52, 0x55, 0x10, 0x1F, 0xAF, 0x27, 0xF0, 0x94, 0x5C, 0xCD, 0x3F, 0x7A, 0x79, 0x9F, 0x2F, 0xF0, 0xE7, 0x45, 0xF0, 0x86, 0x3C, 0xF9, 0xB0, 0xEA, 0x6D, 0x90, 0x42, 0xF7, 0x91, 0xED, 0x3A, 0x9A, 0x7C, 0x01, 0x6B, 0x84, 0xDC, 0x6C, 0xC8, 0x43, 0x07, 0x5C, 0x08, 0xF7, 0xDF, 0xEB, 0xE3, 0xAE, 0xA4] (也就是这 64 位的 4 个 xmmword)
原顺序 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ {}
打乱后的顺序 HfVl {qPcCYNMoRi6D7Jr} espOL3FhwdWAtTGZba4Ugjvnx1QkKE2IS9yuz5BX08m
字符的加密逻辑:
do
{
v59 = (unsigned __int8)flagg[v58] + 1;
LOWORD(v51) = 1;
LOWORD(v52) = 257;
v60 = 0;
do
{
v62 = v52;
LOWORD(v52) = (unsigned __int16)v52 / (unsigned __int16)v59;
v61 = v62 % (unsigned __int16)v59;
v63 = v51;
v51 = v60 - v51 * v52;
LODWORD(v52) = v59;
v59 = (unsigned __int16)(v62 % (unsigned __int16)v59);
v60 = v63;
}
while ( v61 );
v64 = 0;
if ( (__int16)v63 > 0 )
v64 = v63;
flagg[v58] = ((unsigned __int16)(v64 + ((__int16)v63 >> 15) - v63) / 0x101u
+ v63
+ ((unsigned __int16)v63 >> 15)
+ 113) ^ 0x89;
v52 = v58 + 1;
v58 = v52;
}
while ( v52 != 64 );
一阶段脚本
def decrypt(data: list, table: dict): | |
tmp=[] | |
for i in data: | |
tmp.append(table[i]) | |
return tmp | |
def reverse_order(data:list, table: list): | |
tmp = [] | |
for i in range(64): | |
tmp.append(data[table[i]]) | |
return tmp | |
#get crypto table | |
s0_255 = [0xFB, 0x7B, 0x4E, 0xBB, 0x51, 0x15, 0x8D, 0xDB, 0xB0, 0xAC, 0xA5, 0x8E, 0xAA, 0xB2, 0x60, 0xEB, 0x63, 0x5C, 0xDE, 0x42, 0x2B, 0xC6, 0xA6, 0x35, 0x30, 0x43, 0xD6, 0x5F, 0xBD, 0x24, 0xB1, 0xE3, 0x8C, 0xA7, 0xD5, 0x2A, 0x7C, 0x6D, 0x8B, 0x17, 0x9D, 0x83, 0xFE, 0x69, 0x10, 0x59, 0xA9, 0x9E, 0x0F, 0x1C, 0x66, 0x97, 0x5B, 0x61, 0xED, 0xAD, 0xE0, 0xDA, 0x27, 0x06, 0x25, 0xDC, 0x5E, 0xE7, | |
0x41, 0x32, 0xD2, 0xD9, 0x8F, 0xEE, 0xAF, 0x03, 0x93, 0x3A, 0x00, 0xA2, 0xE1, 0xB3, 0xEC, 0x81, 0x9F, 0xCA, 0x58, 0xB7, 0x79, 0xFD, 0x3B, 0xA0, 0x02, 0x0C, 0xCB, 0xA8, 0x80, 0xC0, 0x16, 0x4D, 0x2F, 0x75, 0x71, 0x0A, 0x04, 0x39, 0xFF, 0xC1, 0x9C, 0xAB, 0xEF, 0xA4, 0xD8, 0xE2, 0x14, 0xC2, 0x6C, 0x64, 0x1E, 0x6B, 0x7E, 0x99, 0x2E, 0x09, 0x0B, 0x86, 0x74, 0x6A, 0xC4, 0x2D, 0x4F, 0xF9, | |
0xFA, 0x94, 0xB6, 0x1F, 0x89, 0x6F, 0x5D, 0xE8, 0xEA, 0xB5, 0x5A, 0x65, 0x88, 0xC5, 0x7F, 0x77, 0x11, 0xCF, 0xF1, 0x1B, 0x3F, 0xF4, 0x48, 0x47, 0x12, 0xE4, 0xBA, 0xDF, 0xE9, 0x62, 0x6E, 0xB4, 0x96, 0xCD, 0x13, 0x53, 0x4B, 0x28, 0xD7, 0xD1, 0x33, 0xB8, 0xE6, 0x7A, 0x2C, 0x9B, 0x29, 0x44, 0x52, 0xF7, 0x20, 0xF2, 0x31, 0xD3, 0xB9, 0x40, 0xD0, 0x34, 0xF5, 0x54, 0x1A, 0x01, 0xA1, 0x92, | |
0xFC, 0x85, 0x07, 0xBE, 0xDD, 0xBC, 0x19, 0xF3, 0x36, 0xF6, 0x72, 0x98, 0x4C, 0x7D, 0xC7, 0xD4, 0x45, 0x4A, 0x9A, 0xC3, 0x8A, 0xE5, 0x50, 0x46, 0xCC, 0x68, 0x76, 0x67, 0xC9, 0x0E, 0x3C, 0x57, 0xF0, 0x22, 0xBF, 0x26, 0x84, 0x0D, 0x90, 0xA3, 0xAE, 0x3D, 0x1D, 0xC8, 0x91, 0x05, 0x87, 0x70, 0x08, 0x73, 0x21, 0x49, 0x55, 0x3E, 0x37, 0x23, 0x18, 0x56, 0xCE, 0x82, 0x38, 0x95, 0x78, 0xF8] | |
load_data = [0xA7, 0x51, 0x68, 0x52, 0x85, 0x27, 0xFF, 0x31, 0x88, 0x87, 0xD2, 0xC7, 0xD3, 0x23, 0x3F, 0x52, 0x55, 0x10, 0x1F, 0xAF, 0x27, 0xF0, 0x94, 0x5C, 0xCD, 0x3F, 0x7A, 0x79, 0x9F, 0x2F, 0xF0, 0xE7, 0x45, 0xF0, 0x86, 0x3C, 0xF9, 0xB0, 0xEA, 0x6D, 0x90, 0x42, 0xF7, 0x91, 0xED, 0x3A, 0x9A, 0x7C, 0x01, 0x6B, 0x84, 0xDC, 0x6C, 0xC8, 0x43, 0x07, 0x5C, 0x08, 0xF7, 0xDF, 0xEB, 0xE3, 0xAE, 0xA4] | |
crypto_table = dict(zip(s0_255,range(0x100))) | |
#get order table | |
source = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}' | |
replaced = 'HfVl{qPcCYNMoRi6D7Jr}espOL3FhwdWAtTGZba4Ugjvnx1QkKE2IS9yuz5BX08m' | |
revsere_table = [] #index 是 source 在 replace 中的下标 | |
for i in source: | |
revsere_table.append(replaced.find(i)) | |
tmp = [0x52, 0xCB, 0x15, 0x10, 0x7E, 0xD3, 0x78, 0x26, 0xC2, 0x14, 0x09, 0x50, 0x55, 0xFA, 0xEE, 0xC3, 0x0A, 0x97, 0xB9, 0x38, 0x12, 0x3D, 0x0E, 0xE9, 0xBE, 0xF6, 0x2B, 0x66, 0x67, 0xA8, 0x87, 0xAE, 0x1D, 0x53, 0x62, 0xEC, 0xFC, 0x5C, 0x88, 0x68, 0x23, 0x5B, 0x36, 0x13, 0xFB, 0xD7, 0xCA, 0x7A, 0xBD, 0xD9, 0x69, 0x6A, 0xE4, 0x2A, 0x6C, 0x9D, 0x86, 0xE0, 0xA4, 0x01, 0xBA, 0x3B, 0x20, 0x92] | |
for i in range(256): | |
tmp = decrypt(tmp, crypto_table) | |
tmp = reverse_order(tmp, revsere_table) | |
print(''.join(chr(i) for i in tmp)) | |
# 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{} |
以上脚本用于还原 排序 + 加密,tmp 的值就是经历过完整的打乱顺序和加密后 dump 出的,并不是最终的 cmp_data
说明要么是比较数据还有操作 要么是 input 还有操作
继续往后面看 比较逻辑
like_i = 4LL; | |
do | |
{ | |
v77 = v84; // 0xff | |
if ( v84 < 205 ) | |
goto LABEL_74; | |
*(_DWORD *)v78 = *(_DWORD *)((char *)dest + like_i); | |
v79 = v82; | |
v82[204] = input[like_i]; | |
if ( v77 < 224 ) | |
goto LABEL_74; | |
v79[223] = v78[0]; | |
v79[205] = input[like_i + 1]; | |
if ( v77 == 224 ) | |
goto LABEL_74; | |
v79[224] = v78[1]; | |
v79[206] = input[like_i + 2]; | |
if ( v77 < 226 ) | |
goto LABEL_74; | |
v79[225] = v78[2]; | |
v79[207] = input[like_i + 3]; | |
if ( v77 == 226 ) | |
goto LABEL_74; | |
v79[226] = v78[3]; | |
_$LT$$RF$std..net..tcp..TcpStream$u20$as$u20$std..io..Write$GT$::write::h0bbfc2d1fa700c7a(); | |
if ( v81[0] ) | |
goto LABEL_50; | |
v81[0] = 0LL; | |
v75 = std::io::default_read_exact::h61fb53e2a02eb302(&v85, v81, 8LL); | |
if ( v75 ) | |
goto LABEL_55; | |
if ( !v81[0] ) | |
goto LABEL_70; | |
v80 = (unsigned __int64)(like_i + 1) <= 60; | |
like_i += 4LL; | |
} | |
while ( v80 ); |
每次是赋值了 input 的 4 个字节 和比较数据的四个字节 但是不清楚跑哪里去了
而 v79 [204] v79 [205] v79 [206] v79 [207] 是栈中固定的位置,为什么下标怎么奇怪呢?
通过查找交叉引用可以发现
v79 是一开始 255 字节长的 shellcode
IDA 有些识别不太准确 在汇编中查看
.rodata:000055E808DA1B2B sub_55E808DA1B2B proc near ; DATA XREF: seccomp_shell::shell::verify::h898bf5fa26dafbab+44D↑o
.rodata:000055E808DA1B2B push rsp
.rodata:000055E808DA1B2C pop rbp
.rodata:000055E808DA1B2D xor esi, esi ; flags
.rodata:000055E808DA1B2F mov rcx, 379F3A62B80657A1h
.rodata:000055E808DA1B39 mov rdx, 37F7494DD66F358Eh
.rodata:000055E808DA1B43 xor rcx, rdx
.rodata:000055E808DA1B46 push rcx
.rodata:000055E808DA1B47 push rsp
.rodata:000055E808DA1B48 pop rdi ; filename
.rodata:000055E808DA1B49 push 2
.rodata:000055E808DA1B4B pop rax
.rodata:000055E808DA1B4C cdq ; mode
.rodata:000055E808DA1B4D syscall ; LINUX - sys_open
.rodata:000055E808DA1B4F xchg rax, rdi ; fd
.rodata:000055E808DA1B51 xor eax, eax
.rodata:000055E808DA1B53 push rax
.rodata:000055E808DA1B54 push rsp
.rodata:000055E808DA1B55 pop rsi ; buf
.rodata:000055E808DA1B56 push 4
.rodata:000055E808DA1B58 pop rdx ; count
.rodata:000055E808DA1B59 syscall ; LINUX - sys_read
.rodata:000055E808DA1B5B pop r12
.rodata:000055E808DA1B5D push 3
.rodata:000055E808DA1B5F pop rax
.rodata:000055E808DA1B60 syscall ; LINUX - sys_close
.rodata:000055E808DA1B62 xor esi, esi ; flags
.rodata:000055E808DA1B64 mov rcx, 0AAC06463C36F3B3Bh
.rodata:000055E808DA1B6E mov rdx, 0AAC06463C30B4C48h
.rodata:000055E808DA1B78 xor rcx, rdx
.rodata:000055E808DA1B7B push rcx
.rodata:000055E808DA1B7C mov rcx, 7DA9F8D67582578Ch
.rodata:000055E808DA1B86 mov rdx, 0EC888F916F632A3h
.rodata:000055E808DA1B90 xor rcx, rdx
.rodata:000055E808DA1B93 push rcx
.rodata:000055E808DA1B94 push rsp
.rodata:000055E808DA1B95 pop rdi ; filename
.rodata:000055E808DA1B96 push 2
.rodata:000055E808DA1B98 pop rax
.rodata:000055E808DA1B99 cdq ; mode
.rodata:000055E808DA1B9A syscall ; LINUX - sys_open
.rodata:000055E808DA1B9C xchg rax, rdi ; fd
.rodata:000055E808DA1B9E xor eax, eax
.rodata:000055E808DA1BA0 push rax
.rodata:000055E808DA1BA1 push rsp
.rodata:000055E808DA1BA2 pop rsi ; buf
.rodata:000055E808DA1BA3 push 4
.rodata:000055E808DA1BA5 pop rdx ; count
.rodata:000055E808DA1BA6 syscall ; LINUX - sys_read
.rodata:000055E808DA1BA8 pop r13
.rodata:000055E808DA1BAA push 3
.rodata:000055E808DA1BAC pop rax
.rodata:000055E808DA1BAD syscall ; LINUX - sys_close
.rodata:000055E808DA1BAF xor esi, esi ; flags
.rodata:000055E808DA1BB1 push 6Fh ; 'o'
.rodata:000055E808DA1BB3 mov rcx, 77D9F62D0C06E559h
.rodata:000055E808DA1BBD mov rdx, 5BC8C027A638176h
.rodata:000055E808DA1BC7 xor rcx, rdx
.rodata:000055E808DA1BCA push rcx
.rodata:000055E808DA1BCB push rsp
.rodata:000055E808DA1BCC pop rdi ; filename
.rodata:000055E808DA1BCD push 2
.rodata:000055E808DA1BCF pop rax
.rodata:000055E808DA1BD0 cdq ; mode
.rodata:000055E808DA1BD1 syscall ; LINUX - sys_open
.rodata:000055E808DA1BD3 xchg rax, rdi ; fd
.rodata:000055E808DA1BD5 xor eax, eax
.rodata:000055E808DA1BD7 push rax
.rodata:000055E808DA1BD8 push rsp
.rodata:000055E808DA1BD9 pop rsi ; buf
.rodata:000055E808DA1BDA push 4
.rodata:000055E808DA1BDC pop rdx ; count
.rodata:000055E808DA1BDD syscall ; LINUX - sys_read
.rodata:000055E808DA1BDF pop rax
.rodata:000055E808DA1BE0 not rax
.rodata:000055E808DA1BE3 shr rax, 1Dh
.rodata:000055E808DA1BE7 cqo
.rodata:000055E808DA1BE9 push 29h ; ')'
.rodata:000055E808DA1BEB pop rcx
.rodata:000055E808DA1BEC div rcx
.rodata:000055E808DA1BEF xchg rax, r14
.rodata:000055E808DA1BF1 push 3
.rodata:000055E808DA1BF3 pop rax
.rodata:000055E808DA1BF4 syscall ; LINUX - sys_close
.rodata:000055E808DA1BF6 mov eax, 267814C2h
.rodata:000055E808DA1BFB add eax, r12d
.rodata:000055E808DA1BFE xor eax, r13d
.rodata:000055E808DA1C01 ror eax, 0Bh
.rodata:000055E808DA1C04 not eax
.rodata:000055E808DA1C06 xor eax, r14d
.rodata:000055E808DA1C09 cmp eax, 31FF2788h
.rodata:000055E808DA1C0E jnz short loc_55E808DA1C15
.rodata:000055E808DA1C0E
.rodata:000055E808DA1C10 push 1
.rodata:000055E808DA1C12 pop rax
.rodata:000055E808DA1C13 jmp short loc_55E808DA1C18
.rodata:000055E808DA1C13
.rodata:000055E808DA1C15 ; ---------------------------------------------------------------------------
.rodata:000055E808DA1C15
.rodata:000055E808DA1C15 loc_55E808DA1C15: ; CODE XREF: sub_55E808DA1B2B+E3↑j
.rodata:000055E808DA1C15 xor rax, rax
.rodata:000055E808DA1C15
.rodata:000055E808DA1C18
.rodata:000055E808DA1C18 loc_55E808DA1C18: ; CODE XREF: sub_55E808DA1B2B+E8↑j
.rodata:000055E808DA1C18 push rax
.rodata:000055E808DA1C19 push rbx
.rodata:000055E808DA1C1A pop rdi ; fd
.rodata:000055E808DA1C1B push rsp
.rodata:000055E808DA1C1C pop rsi ; buf
.rodata:000055E808DA1C1D push 8
.rodata:000055E808DA1C1F pop rdx ; count
.rodata:000055E808DA1C20 push 1
.rodata:000055E808DA1C22 pop rax
.rodata:000055E808DA1C23 syscall ; LINUX - sys_write
.rodata:000055E808DA1C25 push rbp
.rodata:000055E808DA1C26 pop rsp
.rodata:000055E808DA1C27 jmp r15
.rodata:000055E808DA1C27
.rodata:000055E808DA1C27 sub_55E808DA1B2B endp
.rodata:000055E808DA1C27
发现之前赋值的 input 和比较数据都跑这里来了
add eax, r12d #首先将 eax 与 r12d 相加
xor eax, r13d #然后将 eax 与 r13d 进行异或运算
ror eax, 0Bh #接着将 eax 向右旋转 11 位
not eax# 然后对 eax 求反
xor eax, r14d# 再将 eax 与 r14d 进行异或运算
关键就是上面几条汇编指令了 对 input 还进行了操作
关键就是找到 r12 r13 r14 的值
但这个 shellcode 在程序中好像没有拿出来执行 (也可能是因为我不会调)
而且这个程序好像就单纯的验证了服务器端和客服端的输入是否相同 有点闹麻
(有一个 std::io::default_read_exact 是从客服端接受,从 IDA 动态调试会卡住也能发现这一点)
分析 shellcode 可以得出 r12 r13 r14 是定值
分析方法:随便找一个简单的程序 用 IDApatch 成 shellcode 执行一下就行了
直接用 lazyIDA paste data
可以发现 r12 r13 r14 的定值是多少 然后就直接写 exp 脚本解得 flag 就 OK 了
import numpy as np | |
def decrypt(data: list, table: dict): | |
tmp=[] | |
for i in data: | |
tmp.append(table[i]) | |
return tmp | |
def reverse_order(data:list, table: list): | |
tmp = [] | |
for i in range(64): | |
tmp.append(data[table[i]]) | |
return tmp | |
# 定义一个函数,接受一个参数,返回解密后的值 | |
def de_cmp(x): | |
# 定义 r12, r13, r14 的值 | |
r12 = 0x0000000464C457F | |
r13 = 0x0000000746F6F72 | |
r14 = 0x000000031F3831F | |
# 将 x 转换为无符号 32 位整数 | |
x = np.uint32(x) | |
# 将 x 与 r14 异或 | |
x = np.uint32(x ^ r14) | |
# 将 x 取反 | |
x = np.uint32(~x) | |
# 将 x 左旋 11 位 | |
x = np.uint32((x << 11) | (x >> (32 - 11))) | |
# 将 x 与 r13 异或 | |
x = np.uint32(x ^ r13) | |
# 将 x 减去 r12 | |
x = np.uint32(x - r12) | |
# 打印 x 的十六进制表示 | |
print(hex(x)) | |
# 将 x 转换为有符号 32 位整数 | |
x = np.int32(x) | |
# 返回 x 的四个字节 | |
return [x & 0xff, (x & 0xff00) >> 8, (x & 0xff0000) >> 16, (x & 0xff000000) >> 24] | |
#get crypto table | |
s0_255 = [0xFB, 0x7B, 0x4E, 0xBB, 0x51, 0x15, 0x8D, 0xDB, 0xB0, 0xAC, 0xA5, 0x8E, 0xAA, 0xB2, 0x60, 0xEB, 0x63, 0x5C, 0xDE, 0x42, 0x2B, 0xC6, 0xA6, 0x35, 0x30, 0x43, 0xD6, 0x5F, 0xBD, 0x24, 0xB1, 0xE3, 0x8C, 0xA7, 0xD5, 0x2A, 0x7C, 0x6D, 0x8B, 0x17, 0x9D, 0x83, 0xFE, 0x69, 0x10, 0x59, 0xA9, 0x9E, 0x0F, 0x1C, 0x66, 0x97, 0x5B, 0x61, 0xED, 0xAD, 0xE0, 0xDA, 0x27, 0x06, 0x25, 0xDC, 0x5E, 0xE7, | |
0x41, 0x32, 0xD2, 0xD9, 0x8F, 0xEE, 0xAF, 0x03, 0x93, 0x3A, 0x00, 0xA2, 0xE1, 0xB3, 0xEC, 0x81, 0x9F, 0xCA, 0x58, 0xB7, 0x79, 0xFD, 0x3B, 0xA0, 0x02, 0x0C, 0xCB, 0xA8, 0x80, 0xC0, 0x16, 0x4D, 0x2F, 0x75, 0x71, 0x0A, 0x04, 0x39, 0xFF, 0xC1, 0x9C, 0xAB, 0xEF, 0xA4, 0xD8, 0xE2, 0x14, 0xC2, 0x6C, 0x64, 0x1E, 0x6B, 0x7E, 0x99, 0x2E, 0x09, 0x0B, 0x86, 0x74, 0x6A, 0xC4, 0x2D, 0x4F, 0xF9, | |
0xFA, 0x94, 0xB6, 0x1F, 0x89, 0x6F, 0x5D, 0xE8, 0xEA, 0xB5, 0x5A, 0x65, 0x88, 0xC5, 0x7F, 0x77, 0x11, 0xCF, 0xF1, 0x1B, 0x3F, 0xF4, 0x48, 0x47, 0x12, 0xE4, 0xBA, 0xDF, 0xE9, 0x62, 0x6E, 0xB4, 0x96, 0xCD, 0x13, 0x53, 0x4B, 0x28, 0xD7, 0xD1, 0x33, 0xB8, 0xE6, 0x7A, 0x2C, 0x9B, 0x29, 0x44, 0x52, 0xF7, 0x20, 0xF2, 0x31, 0xD3, 0xB9, 0x40, 0xD0, 0x34, 0xF5, 0x54, 0x1A, 0x01, 0xA1, 0x92, | |
0xFC, 0x85, 0x07, 0xBE, 0xDD, 0xBC, 0x19, 0xF3, 0x36, 0xF6, 0x72, 0x98, 0x4C, 0x7D, 0xC7, 0xD4, 0x45, 0x4A, 0x9A, 0xC3, 0x8A, 0xE5, 0x50, 0x46, 0xCC, 0x68, 0x76, 0x67, 0xC9, 0x0E, 0x3C, 0x57, 0xF0, 0x22, 0xBF, 0x26, 0x84, 0x0D, 0x90, 0xA3, 0xAE, 0x3D, 0x1D, 0xC8, 0x91, 0x05, 0x87, 0x70, 0x08, 0x73, 0x21, 0x49, 0x55, 0x3E, 0x37, 0x23, 0x18, 0x56, 0xCE, 0x82, 0x38, 0x95, 0x78, 0xF8] | |
#load_data = [0xA7, 0x51, 0x68, 0x52, 0x85, 0x27, 0xFF, 0x31, 0x88, 0x87, 0xD2, 0xC7, 0xD3, 0x23, 0x3F, 0x52, 0x55, 0x10, 0x1F, 0xAF, 0x27, 0xF0, 0x94, 0x5C, 0xCD, 0x3F, 0x7A, 0x79, 0x9F, 0x2F, 0xF0, 0xE7, 0x45, 0xF0, 0x86, 0x3C, 0xF9, 0xB0, 0xEA, 0x6D, 0x90, 0x42, 0xF7, 0x91, 0xED, 0x3A, 0x9A, 0x7C, 0x01, 0x6B, 0x84, 0xDC, 0x6C, 0xC8, 0x43, 0x07, 0x5C, 0x08, 0xF7, 0xDF, 0xEB, 0xE3, 0xAE, 0xA4] | |
crypto_table = dict(zip(s0_255,range(0x100))) | |
#get order table | |
source = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}' | |
replaced = 'HfVl{qPcCYNMoRi6D7Jr}espOL3FhwdWAtTGZba4Ugjvnx1QkKE2IS9yuz5BX08m' | |
revsere_table = [] #index 是 source 在 replace 中的下标 | |
for i in source: | |
revsere_table.append(replaced.find(i)) | |
cmp_data = [0x526851A7, 0x31FF2785, 0xC7D28788, 0x523F23D3, 0xAF1F1055, 0x5C94F027, 0x797A3FCD, 0xE7F02F9F, 0x3C86F045, 0x6DEAB0F9, 0x91F74290, 0x7C9A3AED, 0xDC846B01, 0x0743C86C, 0xDFF7085C, 0xA4AEE3EB] | |
tmp = [] | |
for i in cmp_data: | |
tmp+=de_cmp(i) | |
print(tmp) | |
for i in range(256): | |
tmp = decrypt(tmp, crypto_table) | |
tmp = reverse_order(tmp, revsere_table) | |
print(''.join(chr(i) for i in tmp)) |
其中 python 和 C 语言的位运算有所不同 (对于符号位的处理) 所以需要使用 np 库
运行即可得到 flag
flag 是一个音乐网址 也算是一个小彩蛋吧
# 总结
有关加密算法由于太菜了不会逆 采用的是暴力的做法 相当于是 00-ff 的一个表 感觉以后遇到复杂的运算但又是按位加密的都可以这样做?