0%

2020网鼎杯-第一场pwn跟re部分wp

2020网鼎杯-第一场

re

vm

两种解法,angr一把梭, 自己手动逆

解法1(angr)

angr我觉得比较重要的有三个点

  1. 起始找寻位置
  2. 需要到达的路径
  3. 避免走的路径

起始找寻位置,我们可以选main, 也可以选特定验证函数, 但是需要注意的是, 数据的找寻

需要到达的路径为: success, flag这种成功的路径

避免走的路径便为失败路径

所以我们需要在ida找着三条路.

起初我选了vm_operate这个函数作为起始找寻位置,但是没有成功, 后面我选了main函数

需要到达的路径便为输出flag处

避免走的路径则是exit这种

最后执行即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
# coding=utf-8
import angr

p = angr.Project('./signal.exe')
good = (0x0040179E)
bad = (0x004016E6)

start = 0x00401760

state = p.factory.blank_state(addr=start)
simgr = p.factory.simulation_manager(state)
simgr.explore(find=good, avoid=bad)
result = simgr.found[0]

for i in range(3):
print (result.posix.dumps(i))

不用一会就拿到flag了

757515121f3d478

解法2(手逆)

table如下

1
[0x0000000A, 0x00000004, 0x00000010, 0x00000008, 0x00000003, 0x00000005, 0x00000001, 0x00000004, 0x00000020, 0x00000008, 0x00000005, 0x00000003, 0x00000001, 0x00000003, 0x00000002, 0x00000008, 0x0000000B, 0x00000001, 0x0000000C, 0x00000008, 0x00000004, 0x00000004, 0x00000001, 0x00000005, 0x00000003, 0x00000008, 0x00000003, 0x00000021, 0x00000001, 0x0000000B, 0x00000008, 0x0000000B, 0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002, 0x00000051, 0x00000008, 0x00000004, 0x00000024, 0x00000001, 0x0000000C, 0x00000008, 0x0000000B, 0x00000001, 0x00000005, 0x00000002, 0x00000008, 0x00000002, 0x00000025, 0x00000001, 0x00000002, 0x00000036, 0x00000008, 0x00000004, 0x00000041, 0x00000001, 0x00000002, 0x00000020, 0x00000008, 0x00000005, 0x00000001, 0x00000001, 0x00000005, 0x00000003, 0x00000008, 0x00000002, 0x00000025, 0x00000001, 0x00000004, 0x00000009, 0x00000008, 0x00000003, 0x00000020, 0x00000001, 0x00000002, 0x00000041, 0x00000008, 0x0000000C, 0x00000001, 0x00000007, 0x00000022, 0x00000007, 0x0000003F, 0x00000007, 0x00000034, 0x00000007, 0x00000032, 0x00000007, 0x00000072, 0x00000007, 0x00000033, 0x00000007, 0x00000018, 0x00000007, 0xFFFFFFA7, 0x00000007, 0x00000031, 0x00000007, 0xFFFFFFF1, 0x00000007, 0x00000028, 0x00000007, 0xFFFFFF84, 0x00000007, 0xFFFFFFC1, 0x00000007, 0x0000001E, 0x00000007, 0x0000007A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000]

根据虚拟机指令集可进行一步步进行翻译

image-20200510205225830

10为输入, 所以一开头是输入, table[0]就是10

然后照着指令翻译成handler, 同时最后有个比较

image-20200510211716741

从这里截取每次的结果, 注意,那个je不能让他进去,我输入的是错误的,所以当然会进je底下代码,这里改成jmp

image-20200510211829236

1
0x22,0x3F,0x34,0x32,0x72,0x33,0x18,0xA7,0x31,0xF10x28,0x840xC1,0x1E,0x7A

handler为> 0xC前的合体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
A 4

8 3 5 1 4 8

5 3 1 3 2 8 B 1 C 8 4 4 1 5 3 8 3

1 B 8 B 1 4 9 8 3

1 2

8 4

1 2

8 5 1 1 5 3 8 2

1 4 9 8 3

1 2

8 C 1 7

取出大体, 在进行细分, 进行一步步算最终结果, 这里可以动态调试, 我就用动态调试的,先打印出我的输入

flag{123456789}

image-20200510213841920

在od里看eax什么时候变成这个的时候注意了,

image-20200510203350281

最后就是下面的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
flag[0]^ 0x10 - 5 = 0x22
(flag[1]^ 0x20)*3 = 0x3F
flag[2] - 2 - 1 = 0x34
(flag[3]+1 )^4 = 0x32
flag[4]*3- 0x21 = 0x72
flag[5]-1-1 = 0x33
9^flag[6]-20 = 0x18
(0x51 + flag[7]) ^ 0x24 = 0xA7
flag[8] + 1 - 1 = 0x31
2 * flag[9] + 0x25 = 0xF1
(0x36 + flag[10]) ^ 0x41 = 0x28
(0x20 + flag[11]) * 1= 0x84
3*flag[12] + 0x25 = 0xC1
9^flag[13] - 0x20= 0x1E
0x41 + flag[14] + 1 = 0x7A

求出结果

最后加上flag{757515121f3d478}

joker

解法1(动态调试)

全程单步的一道题,

image-20200518190720212

长度为0x18前面跟了我好久,两个假的,我在那逆算法,后面追到这里面才是对的

F7跟进,里面不能下断,

image-20200518190850216

到这里发觉就是个异或, 然后跑完前0x13位flag到最后卡死了,那个地方,给了5个东西%tp&:

image-20200518191001739

不知道是啥, 然后那里判断过不去的, 结合srand, 猜测是对这5个数做了操作,盲猜为xor 毕竟只有xor能让你猜了,爆破一下,最后一位为} `

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
code = "hahahaha_do_you_find_me?"

List = [0xE, 0xD, 0x9, 0x6, 0x13, 0x05, 0x58,0x56, 0x3E, 0x6, 0xC, 0x3C, 0x1F, 0x57, 0x14,0x6B,0x57,0x59,0xD]

flag = ""
for i in range(len(List)):
flag += chr(List[i] ^ ord(code[i]))
print(flag)
code = '%tp&:'
temp = ""
for i in range(0, 128):
temp = ""
for j in range(len(code)):
temp += chr(ord(code[j])^i)
if temp[-1:] == "}":
flag += temp
break
print(temp)

print(flag)

解法2(idc脚本修改,静态查看)

1
2
3
4
start = 0x401500
for i in range(186):
c = Byte(start+i)^0x41;
PatchByte(start+i, c)

修改后啦一大片按code, 强制转换为code, 后面有个JMPOUT, 按U取消函数定义, 就可以F5了

image-20200512120802503

很明显, 最后一部分

image-20200512120841953

这里就是5个字符,猜异或

解法3(动态dump内存)

等他smc解密完成后

image-20200518202455155

这里可以脱掉后,打开ida

image-20200518202422654

也可以拿到完美的表现

boom

原来用内存dump的, 那种方式好麻烦… 步骤都好复杂,请教了下虾哥, 他用frida dump dex

安装frida

1
2
pip install frida
pip install frida-tools

下载frida-server

雷电模拟器选x86

开启模拟器,adb连接上

1
2
3
4
5
6
7
adb devices 
adb push .\frida-server-x86 /data/local/tmp
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
# 后面可以 adb shell进去,linux操作也行
adb shell chmod +x /data/local/tmp/frida-server-x86
adb shell /data/local/tmp/frida-server-x86

image-20200518190929442

开启frida-server后,运行app

1
frida-ps -U

查看是否有进程

image-20200518191033298

下载frida-dex-dump

确认app 运行起来了, 并且apk放在跟main.py同目录下, 运行main.py, 这里会报错,先安装click

1
pip install click

1
python main.py

脱完壳有dex

image-20200519193223853

jadx打开dex

image-20200518191100669

pwn

boom1

打开ida查看,发觉有点乱, 代码数量庞大, 只能看字符串

image-20200510215551516

看到这里,发觉他有个main函数, 猜测, 这应该是一个编译器, 猜测仅仅是不够的, 验证, 拿LEA IMM JMP JSP BZ BNZ,在github对应code里搜寻一番,找到了源码

编译c语言代码的, 既然有编译器了, 那我们不是可以为所欲为,

首先说明,这个傻逼编译器不支持挺多东西的

不支持的东西我测试出来如下

  1. 变量声明的同时赋值, 需要分步,也就是先声明,后使用
  2. 不允许有两个printf语句, 有的话会报not allow
  3. 不允许prinf在执行语句中间,只能在最后面

第一步,打印下变量地址

1
2
3
4
5
6
int main()
{
int local;
int* __free_hook;
printf("%p\n", &local);
}

这个下断点可以利用

1
2
3
b printf
c
finish

这样输出结果同时,我们还可以vmmap看下这块内存在哪

image-20200511234547064

这里计算出固定偏移为0x523fd8,直接改__free_hook为one_gadget

1
2
3
4
5
6
7
8
9
int main()
{
int local;
int* __free_hook;
local = (int)&local - 0x523fd8;
__free_hook = local + 0x3c67a8;
*__free_hook = local + 0x4526a;
free("1");
}

boom2

逆向还没逆出来,待更

本文作者:NoOne
本文地址https://noonegroup.xyz/posts/72106f6e/
版权声明:转载请注明出处!