Confused

定位关键逻辑

首先分析到这个sub_1000011D0是关键函数是没有什么问题的,直接shift+f12定位DDCTF的字符串就到了这一部分逻辑

1
2
3
4
if ( (unsigned int)sub_1000011D0(*((__int64 *)&v14 + 1)) == 1 )
objc_msgSend(v17, "onSuccess");
else
objc_msgSend(v17, "onFailed");

简要分析确定是虚拟机

跟进去以后发现

1
2
3
4
5
6
7
8
9
10
__int64 __fastcall sub_1000011D0(__int64 a1)
{
char v2; // [rsp+20h] [rbp-C0h]
__int64 v3; // [rsp+D8h] [rbp-8h]

v3 = a1;
memset(&v2, 0, 0xB8uLL);
sub_100001F60(&v2, a1);
return (unsigned int)sub_100001F00(&v2);
}

这个函数首先分配了一个0xb8大小的空间,然后填充为0x00,然后把这个v2传入了一个sub_100001F60函数
跟进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__int64 __fastcall sub_100001F60(__int64 a1, __int64 a2)
{
*(_DWORD *)a1 = 0;
*(_DWORD *)(a1 + 4) = 0;
*(_DWORD *)(a1 + 8) = 0;
*(_DWORD *)(a1 + 12) = 0;
*(_DWORD *)(a1 + 16) = 0;
*(_DWORD *)(a1 + 176) = 0;
*(_BYTE *)(a1 + 32) = 0xF0u;
*(_QWORD *)(a1 + 40) = sub_100001D70;
*(_BYTE *)(a1 + 48) = 0xF1u;
*(_QWORD *)(a1 + 56) = sub_100001A60;
*(_BYTE *)(a1 + 64) = 0xF2u;
*(_QWORD *)(a1 + 72) = sub_100001AA0;
*(_BYTE *)(a1 + 80) = 0xF4u;
*(_QWORD *)(a1 + 88) = sub_100001CB0;
*(_BYTE *)(a1 + 96) = 0xF5u;
*(_QWORD *)(a1 + 104) = sub_100001CF0;
*(_BYTE *)(a1 + 112) = 0xF3u;
*(_QWORD *)(a1 + 120) = sub_100001B70;
*(_BYTE *)(a1 + 128) = 0xF6u;
*(_QWORD *)(a1 + 136) = sub_100001B10;
*(_BYTE *)(a1 + 144) = 0xF7u;
*(_QWORD *)(a1 + 152) = sub_100001D30;
*(_BYTE *)(a1 + 160) = 0xF8u;
*(_QWORD *)(a1 + 168) = sub_100001C60;
qword_100003F58 = malloc(0x400uLL);
return __memcpy_chk((char *)qword_100003F58 + 48, a2, 18LL, -1LL);
}

首先先按H-16这些改成unsigned _int8类型,看着方便。
这里在对一个结构体进行初始化,前几个都是DWORD类型,后几个可以看出一定的规律,都是一个BYTE类型的F几和一个函数指针,实际上这里还可以写一个小的结构体,结构体包含F几和函数指针,这是一个绑定操作,将F几与函数绑定。

返回去查看sub_100001F00函数里调用的第二个函数

1
2
3
4
5
6
7
8
__int64 __fastcall sub_100001F00(__int64 a1)
{
*(_QWORD *)(a1 + 24) = (char *)&loc_100001980 + 4;
while ( **(unsigned __int8 **)(a1 + 24) != 243 )
sub_100001E50(a1);
free(qword_100003F58);
return *(unsigned int *)(a1 + 176);
}

这里把*(_QWORD *)(a1 + 24)指向(char *)&loc_100001980 + 4处,然后调用了sub_100001E50sub_100001E50里通过(char *)&loc_100001980 + 4处的值来确定调用刚才上一个函数中赋值的哪一个函数指针指向的函数,显然这是一个分发器,*(_QWORD *)(a1 + 24)就是指令指针寄存器,(char *)&loc_100001980 + 4处就是虚拟机字节码。

这道题就是考的虚拟机。

handler分析

然后对初始化的结构体和opcode对应的每个函数简要分析

0xF0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
__int64 __fastcall sub_100001D70(__int64 a1)
{
__int64 result; // rax
signed int *v2; // [rsp+Ch] [rbp-18h]

v2 = (signed int *)(*(_QWORD *)(a1 + 24) + 2LL);
switch ( *(unsigned __int8 *)(*(_QWORD *)(a1 + 24) + 1LL) )
{
case 0x10u:
*(_DWORD *)a1 = *v2;
break;
case 0x11u:
*(_DWORD *)(a1 + 4) = *v2;
break;
case 0x12u:
*(_DWORD *)(a1 + 8) = *v2;
break;
case 0x13u:
*(_DWORD *)(a1 + 12) = *v2;
break;
case 0x14u:
*(_DWORD *)a1 = *((char *)qword_100003F58 + *v2);
break;
default:
break;
}
result = a1;
*(_QWORD *)(a1 + 24) += 6LL;
return result;
}

可以看到v2是虚拟机指令指针寄存器指向的地方后面第二个字节的内容,然后有一个switch结构,是在判断虚拟机指令后面第一个字节指定的是哪个虚拟机寄存器,然后将v2赋值到寄存器里,如果是0x14的话就把*((char *)qword_100003F58 + *v2)赋值给第一个寄存器,猜测这qword_100003F58相当于虚拟机的栈,然后函数负责移动指令指针寄存器,这个函数对应的虚拟机指令占六个字节,所以在最后有*(_QWORD *)(a1 + 24) += 6LL;所以这个函数的功能就是把一个立即数传入虚拟机的一个寄存器中

0xF1

1
2
3
4
5
6
7
8
9
__int64 __fastcall sub_100001A60(__int64 a1)
{
__int64 result; // rax

result = (unsigned int)(*(_DWORD *)(a1 + 4) ^ *(_DWORD *)a1);
*(_DWORD *)a1 = result;
++*(_QWORD *)(a1 + 24);
return result;
}

可以看到他把前两个寄存器里的值进行异或然后又把结果放到了第一个寄存器中,同样的这个虚拟机指令占一个字节,此函数负责将指令指针寄存器加1字节

0xF2 & 0xF6

0xF2对应的函数和0xF6对应的函数要结合来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall sub_100001AA0(__int64 a1)
{
__int64 result; // rax

*(_DWORD *)(a1 + 16) = *(_DWORD *)a1 == *((char *)qword_100003F58 + *(unsigned __int8 *)(*(_QWORD *)(a1 + 24) + 1LL));
result = a1;
*(_QWORD *)(a1 + 24) += 2LL;
return result;
}

__int64 __fastcall sub_100001B10(__int64 a1)
{
__int64 result; // rax

if ( *(_DWORD *)(a1 + 16) )
*(_DWORD *)(a1 + 16) = 0;
else
*(_QWORD *)(a1 + 24) += *(unsigned __int8 *)(*(_QWORD *)(a1 + 24) + 1LL);
result = a1;
*(_QWORD *)(a1 + 24) += 2LL;
return result;
}

可以看到0xF2中他把一个表达式的结果放到了*(_DWORD *)(a1 + 16)里,0xF6中他又根据*(_DWORD *)(a1 + 16)移动指令指针寄存器,这就很明显了,这个寄存器的功能就是标志寄存器,存放对比的结果,然后后面一个函数就是条件跳转指令了

0xF4

1
2
3
4
5
6
7
8
9
__int64 __fastcall sub_100001CB0(__int64 a1)
{
__int64 result; // rax

result = (unsigned int)(*(_DWORD *)(a1 + 4) + *(_DWORD *)a1);
*(_DWORD *)a1 = result;
++*(_QWORD *)(a1 + 24);
return result;
}

很明显的看出这个实在做加法运算,把前两个寄存器的值加起来放到第一个寄存器里

0xF5

1
2
3
4
5
6
7
8
9
__int64 __fastcall sub_100001CF0(__int64 a1)
{
__int64 result; // rax

result = (unsigned int)(*(_DWORD *)a1 - *(_DWORD *)(a1 + 4));
*(_DWORD *)a1 = result;
++*(_QWORD *)(a1 + 24);
return result;
}

与上面的函数类似,这个是减法

0xF3

跟进0xF3对应的函数发现只有一对花括号,于是取名为nop

0xF7

1
2
3
4
5
6
7
8
9
__int64 __fastcall sub_100001D30(__int64 a1)
{
__int64 result; // rax

result = *(unsigned int *)(*(_QWORD *)(a1 + 24) + 1LL);
*(_DWORD *)(a1 + 176) = result;
*(_QWORD *)(a1 + 24) += 5LL;
return result;
}

可以看到他把操作数放进了(_DWORD *)(a1 + 176)处,先不管他是在干啥,先看0xF8

0xF8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
__int64 __fastcall sub_100001C60(__int64 a1)
{
__int64 result; // rax

result = sub_100001B80((unsigned int)(char)*(_DWORD *)a1, 2LL);
*(_DWORD *)a1 = (char)result;
++*(_QWORD *)(a1 + 24);
return result;
}

__int64 __fastcall sub_100001B80(char a1, int a2)
{
bool v3; // [rsp+7h] [rbp-11h]
bool v4; // [rsp+Fh] [rbp-9h]
char v5; // [rsp+17h] [rbp-1h]

v4 = 0;
if ( a1 >= 65 )
v4 = a1 <= 90;
if ( v4 )
{
v5 = (a2 + a1 - 65) % 26 + 65;
}
else
{
v3 = 0;
if ( a1 >= 97 )
v3 = a1 <= 122;
if ( v3 )
v5 = (a2 + a1 - 97) % 26 + 97;
else
v5 = a1;
}
return (unsigned int)v5;
}

可以看到他调用了下级函数sub_100001B80,显然这个sub_100001B80函数是在对第一个虚拟机寄存器中的内容进行移位为2的凯撒移位,首先他通过ascii码的范围判断是大写还是小写,以确保大小写字母都能够通用

题解

分析完了这几个函数和几个寄存器以后,可以创建一个vm_cpu结构体给函数和寄存器都取个名字,然后把第一个函数初始化的结构体用模板解析,看起来比较清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__int64 __fastcall init_vm(vm_cpu *a1, __int64 a2)
{
a1->vm_r1 = 0;
a1->vm_r2 = 0;
a1->vm_r3 = 0;
a1->vm_r4 = 0;
a1->flag = 0;
a1->myeip = 0;
LOBYTE(a1->opcode_f0) = 0xF0u;
a1->mov_reg_imm = (__int64)mov_reg_imm;
LOBYTE(a1->opcode_f1) = 0xF1u;
a1->xor_r1_r2 = (__int64)xor_r1_r2;
LOBYTE(a1->opcode_f2) = 0xF2u;
a1->cmp_r1_imm = (__int64)cmp_r1_imm;
LOBYTE(a1->opcode_f4) = 0xF4u;
a1->add_r1_r2 = (__int64)add_r1_r2;
LOBYTE(a1->opcode_f5) = 0xF5u;
a1->dec_r1_r2 = (__int64)sub_r1_r2;
LOBYTE(a1->opcode_f3) = 0xF3u;
a1->nop = (__int64)nop;
LOBYTE(a1->opcode_f6) = 0xF6u;
a1->jz_imm = (__int64)jz_imm;
LOBYTE(a1->opcode_f7) = 0xF7u;
a1->mov_buff_imm = (__int64)mov_buff_imm;
LOBYTE(a1->opcode_f8) = 0xF8u;
a1->shift_r1_2 = (__int64)shift_r1_2;
qword_100003F58 = malloc(0x400uLL);
return __memcpy_chk((char *)qword_100003F58 + 48, a2, 18LL, -1LL);
}

idapython一句话解题

这里既然我们已经了解了这些opcode的功能,其实并不需要费力去提取出代码执行或者写脚本一句一句翻译了
他就是把一个立即数装入第一个寄存器中,然后判断大小写,对其进行凯撒移位,我们直接在idapython里写一句脚本便出来了

1
"".join([chr(0x41+(int(i[-2:],16)+2-0x41)%26) if int(i[-2:],16)<0x61 else chr(0x61+(int(i[-2:],16)+2-0x61)%26) for i in re.findall("f010..",get_bytes(0x100001984,3000).encode("hex"))])

翻译字节码脚本

当然也可以写个脚本把整个虚拟机字节码全部翻译出来
首先可以用get_bytes(0x100001984,3000,0).encode("hex")把虚拟机字节码给提取出来,也可以用lazyida插件
然后解析代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import re
loc_100001980="f01066000000f8f230f6c1f01063000000f8f231f6b6f0106a000000f8f232f6abf0106a000000f8f233f6a0f0106d000000f8f234f695f01057000000f8f235f68af0106d000000f8f236f67ff01073000000f8f237f674f01045000000f8f238f669f0106d000000f8f239f65ef01072000000f8f23af653f01052000000f8f23bf648f01066000000f8f23cf63df01063000000f8f23df632f01044000000f8f23ef627f0106a000000f8f23ff61cf01079000000f8f240f611f01065000000f8f241f606f701000000f3f700000000f35dc30f1f840000000000554889e548897df8488b7df88b078945f4488b7df88b47048945f08b45f43345f0488b7df88907488b7df8488b4f184883c10148894f185dc30f1f8000000000554889e548897df8488b7df88b078945f4488b7df8488b7f180fb64701488b3d942400004863c84801cf48897de88b45f4488b4de80fbe1139d00f8510000000488b45f8c7401001000000e90b000000488b45f8c7401000000000488b45f8488b48184883c102488948185dc30f1f00554889e548897df8488b7df8488b7f188a47018845f7488b7df8837f10000f851b0000000fb645f7488b4df8488b51184863f04801f248895118e90b000000488b45f8c7401000000000488b45f8488b48184883c102488948185dc30f1f4000554889e548897df85dc3660f1f440000554889e54088f88845fe8975f831c088c10fbe45fe83f841884df70f8c0d0000000fbe45fe83f85a0f9ec1884df78a45f7a8010f8505000000e929000000b81a0000000fbe4dfe83e941034df88945f089c8998b4df0f7f983c2414088d6408875ffe965000000e90000000031c088c10fbe45fe83f861884def0f8c0d0000000fbe45fe83f87a0f9ec1884def8a45efa8010f8505000000e929000000b81a0000000fbe4dfe83e961034df88945e889c8998b4de8f7f983c2614088d6408875ffe9060000008a45fe8845ff0fbe45ff5dc366666666662e0f1f840000000000554889e54883ec10be0200000048897df8488b7df88b0788c1884df70fbe7df7e8fbfeffff0fbef0488b55f88932488b55f84c8b42184983c0014c8942184883c4105dc36666662e0f1f840000000000554889e548897df8488b7df88b078945f4488b7df88b47048945f08b45f40345f0488b7df88907488b7df8488b4f184883c10148894f185dc30f1f8000000000554889e548897df8488b7df88b078945f4488b7df88b47048945f08b45f42b45f0488b7df88907488b7df8488b4f184883c10148894f185dc30f1f8000000000554889e548897df8488b7df8488b7f184883c70148897df0488b7df08b07488b7df88987b0000000488b7df8488b4f184883c10548894f185dc3660f1f440000554889e548897df8488b7df8488b7f1848ffc748897df0488b7df8488b7f184883c70248897de8488b7df00fb60783c0f089c783e80448897de08945dc0f8773000000488d057e000000488b4de0486314884801c2ffe2488b45e88b08488b45f88908e94e000000488b45e88b08488b45f8894804e93c000000488b45e88b08488b45f8894808e92a000000488b45e88b08488b45f889480ce918000000488b0543210000488b4de84863090fbe1408488b45f88910488b45f8488b48184883c106488948185dc38fffffffa0ffffffb2ffffffc4ffffffd6ffffff0f1f4000554889e54883ec2048897df8c745f400000000c745f00000000031c088c1837df400884def0f850a000000837df0090f9cc08845ef8a45efa8010f8505000000e963000000488b45f8488b40180fb608488b45f84883c020486355f048c1e2044801d00fb63039f10f852c000000c745f401000000488b45f84883c02048634df048c1e1044801c8488b4008488b4df84889cfffd0e9090000008b45f083c0018945f0e972ffffff4883c4205dc36690554889e54883ec10488d0571faffff4883c00448897df8488b7df848894718488b45f8488b40180fb60881f9f30000000f840e000000488b7df8e811ffffffe9dbffffff488b3d0d200000e880020000488b7df88b87b00000004883c4105dc3554889e54157415641554154534883ec18b80004000089c1488d15e1fcffff4c8d05aafdffff4c8d0d83fbffff4c8d15dcfbffff4c8d1d55fdffff488d1d0efdffff4c8d35f7faffff4c8d3db0faffff4c8d25b9fdffff48897dd0488975c8488b75d0c70600000000488b75d0c7460400000000488b75d0c7460800000000488b75d0c7460c00000000488b75d0c7461000000000488b75d0c786b000000000000000488b75d0c64620f0488b75d04c896628488b75d0c64630f1488b75d04c897e38488b75d0c64640f2488b75d04c897648488b75d0c64650f4488b75d048895e58488b75d0c64660f5488b75d04c895e68488b75d0c64670f3488b75d04c895678488b75d0c68680000000f6488b75d04c898e88000000488b75d0c68690000000f7488b75d04c898698000000488b75d0c686a0000000f8488b75d0488996a80000004889cfe82901000041bd120000004489ea48c7c1ffffffff488905941e0000488b058d1e00004883c030488b75c84889c7e8ef000000488945c04883c4185b415c415d415e415f5dc39090554889e54883ec20488d45e848897df8488975f048c745e8000000004889c74889d6e8ef00000031c989ce488d45e84889c7e8df0000004883c4205dc30f1f00554889e54883ec20488d45e848897df8488975f048c745e8000000004889c74889d6e8af00000031c989ce488d45e84889c7e89f00000041b001410fbec04883c4205dc36666662e0f1f840000000000554889e54883ec20488d45e848897df8488975f048c745e8000000004889c74889d6e85f00000031c989ce488d45e84889c7e84f0000004883c4205dc3ffff255c0e0000ff255e0e0000ff25600e0000ff25620e0000ff25640e0000ff25660e0000ff25680e0000ff256a0e0000ff256c0e0000ff256e0e0000ff25700e0000ff25720e0000ff25740e0000ff25760e0000ff25780e00004c8d1de90d00004153ff25d90d0000906819000000e9e6ffffff6862000000e9dcffffff6885000000e9d2ffffff689d000000e9c8ffffff68ba000000e9beffffff68d4000000e9b4ffffff68f2000000e9aaffffff681c010000e9a0ffffff6835010000e996ffffff684c010000e98cffffff6826000000e982ffffff683a000000e978ffffff6846000000e96effffff6854000000e964ffffff6800000000e95affffff766965774469644c6f616400736574526570726573656e7465644f626a6563743a0070776400737472696e6756616c7565006861735072656669783a006c656e67746800737562737472696e6746726f6d496e6465783a006973457175616c546f537472696e673a00737562737472696e675769746852616e67653a0055544638537472696e67006f6e4661696c6564006f6e5375636365737300616c6c6f6300696e697400736574416c6572745374796c653a00616464427574746f6e576974685469746c653a007365744d657373616765546578743a00736574496e666f726d6174697665546578743a007368617265644170706c69636174696f6e006b657957696e646f7700626567696e53686565744d6f64616c466f7257696e646f773a636f6d706c6574696f6e48616e646c65723a00636865636b436f64653a002e6378785f6465737472756374007365745077643a005f707764006973457175616c3a00636c6173730073656c6600706572666f726d53656c6563746f723a00706572666f726d53656c6563746f723a776974684f626a6563743a00706572666f726d53656c6563746f723a776974684f626a6563743a776974684f626a6563743a00697350726f78790069734b696e644f66436c6173733a0069734d656d6265724f66436c6173733a00636f6e666f726d73546f50726f746f636f6c3a00726573706f6e6473546f53656c6563746f723a0072657461696e0072656c65617365006175746f72656c656173650072657461696e436f756e74007a6f6e650068617368007375706572636c617373006465736372697074696f6e0064656275674465736372697074696f6e006170706c69636174696f6e53686f756c6454"
loc_100001980=re.findall("..",loc_100001980)
#print(loc_100001980)
a=0
c=[
["f0",6,lambda x:str("mov r"+x[1]+",0x"+x[2])],
["f1",1,lambda x:str("xor r10,r11")],
["f2",2,lambda x:str("cmp r10,byte ptr ss:[0x"+x[1]+"]")],
["f3",1,lambda x:"nop"],
["f4",1,lambda x:str("add r10,r11")],
["f5",1,lambda x:str("sub r10,r11")],
["f6",2,lambda x:str("jz "+x[1])],
["f7",5,lambda x:str("mov buf,imm")],
["f8",1,lambda x:str("caesar encode r10,2")]
]
while 1:
for i in c:
if(loc_100001980[a] in i):
print(i[2](loc_100001980[a:a+i[1]]))
#print(loc_100001980[a:a+i[1]])
a+=i[1]
break

运行后效果如下,极大的增强了可读性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
mov r10,0x66
caesar encode r10,2
cmp r10,byte ptr ss:[0x30]
jz c1
mov r10,0x63
caesar encode r10,2
cmp r10,byte ptr ss:[0x31]
jz b6
mov r10,0x6a
caesar encode r10,2
cmp r10,byte ptr ss:[0x32]
jz ab
mov r10,0x6a
caesar encode r10,2
cmp r10,byte ptr ss:[0x33]
jz a0
mov r10,0x6d
caesar encode r10,2
cmp r10,byte ptr ss:[0x34]
jz 95
mov r10,0x57
caesar encode r10,2
cmp r10,byte ptr ss:[0x35]
jz 8a
mov r10,0x6d
caesar encode r10,2
cmp r10,byte ptr ss:[0x36]
jz 7f
mov r10,0x73
caesar encode r10,2
cmp r10,byte ptr ss:[0x37]
jz 74
mov r10,0x45
caesar encode r10,2
cmp r10,byte ptr ss:[0x38]
jz 69
mov r10,0x6d
caesar encode r10,2
cmp r10,byte ptr ss:[0x39]
jz 5e
mov r10,0x72
caesar encode r10,2
cmp r10,byte ptr ss:[0x3a]
jz 53
mov r10,0x52
caesar encode r10,2
cmp r10,byte ptr ss:[0x3b]
jz 48
mov r10,0x66
caesar encode r10,2
cmp r10,byte ptr ss:[0x3c]
jz 3d
mov r10,0x63
caesar encode r10,2
cmp r10,byte ptr ss:[0x3d]
jz 32
mov r10,0x44
caesar encode r10,2
cmp r10,byte ptr ss:[0x3e]
jz 27
mov r10,0x6a
caesar encode r10,2
cmp r10,byte ptr ss:[0x3f]
jz 1c
mov r10,0x79
caesar encode r10,2
cmp r10,byte ptr ss:[0x40]
jz 11
mov r10,0x65
caesar encode r10,2
cmp r10,byte ptr ss:[0x41]
jz 06
mov buff,imm
nop
mov buff,imm
nop

Obfuscating macros

ubuntu64 + gdbserver + ida64调试环境

首先main函数里关键的函数只有两个,一个sub_4069D6一个sub_4013E6,先跟进第一个函数,在所有传入我们输入的变量的地方下断点
然后配置好远程gdb调试器,f9运行起来,随手输入ABCDida中触发断点,首先先观察寄存器的值,在堆栈中找到我们输入的字符串

然后一路f9观察对我们输入的字符串做了什么
f9几次后会发现他把我们输入的ABCD转换成了0xABCD

继而我们推测下一个函数会对0xABCD进一步处理或者与一些值进行比较,于是删除所有其他的断点,在0xABCD处下内存读写断点,然后f9运行,就触发了硬件断点

第一次test al,al是检测输入是否为空,再按f9触发第二次断点,会发现如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:0000000000405FA3 loc_405FA3:
.text:0000000000405FA3 mov rax, [rbp+var_220]
.text:0000000000405FAA lea rdx, [rax+1]
.text:0000000000405FAE mov [rbp+var_220], rdx
.text:0000000000405FB5 movzx edx, byte ptr [rax]
.text:0000000000405FB8 mov rax, [rbp+var_210]
.text:0000000000405FBF movzx eax, byte ptr [rax]
.text:0000000000405FC2 mov ecx, eax
.text:0000000000405FC4 mov eax, edx
.text:0000000000405FC6 sub ecx, eax
.text:0000000000405FC8 mov eax, ecx
.text:0000000000405FCA mov edx, eax
.text:0000000000405FCC mov rax, [rbp+var_210]
.text:0000000000405FD3 mov [rax], dl
.text:0000000000405FD5 mov rax, [rbp+var_280]
.text:0000000000405FDC test rax, rax
.text:0000000000405FDF jnz short loc_

f80000000000405FC6处会发现他拿我们的0xAB与一个0x79进行了对比,这样的话我们只需要在sub这里下一个断点,一路f9就好了
他一开始没有验证长度,直接进行了对比,只要对比到不同就退出,因为我们输入了ABCD,观察到他对比一次就会退出了
这样我们就可以每次多输入两个字符,并且这两个字符只能是09AF,然后取出新的对比的字符再多输入两个字符直到他不在对比就得到flag

这道题目还是说,如果没有先调试得到的知识背景,用pintool解题也会卡壳的,因为经过我们的分析,发现第一个函数把字符串ABCD转换成了0xABCD,这里四个字节变成了两个字节,如果用pintool解题需要两个字符两个字符得组合得测试,这个题目不是纯正的ollvm混淆的,大体的思路把所有的涉及到用户输入的地方都下断点,还有就是内存断点的使用,还是很好用的。

题目附件

链接:https://pan.baidu.com/s/1yx9RlgITi5rjcHGBgZ1DDA
提取码:ql9p

参考链接

https://www.52pojie.cn/forum.php?mod=viewthread&tid=860237&page=1