headache

runwu2204 Lv6

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[264]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v5; // [rsp+118h] [rbp-8h]

v5 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
printf("Enter flag: ");
__isoc99_scanf("%s", s);
if ( strlen(s) == 61 )
{
if ( (unsigned int)sub_401290(s) )
puts("Correct!");
else
puts("Wrong!");
return 0LL;
}
else
{
puts("Wrong!");
return 0LL;
}
}

sub_401290(s)作为flag校验函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__int64 __fastcall sub_401290(_BYTE *a1)
{
_DWORD *v2; // rsi

if ( (*a1 ^ a1[25]) != 86 )//对flag进行校验
return 0LL;
v2 = &loc_4012A4;
do
*v2++ ^= 0xEA228DE6;//对函数进行解密
while ( *(v2 - 1) != -366834202 );
return ((__int64 (*)(void))loc_4012A4)();
}
//汇编代码
/*.text:0000000000404374 loc_404374: ; CODE XREF: sub_401290+B↑j
.text:0000000000404374 mov eax, 0EA228DE6h
.text:0000000000404379 lea rsi, loc_4012A4
.text:0000000000404381
.text:0000000000404381 loc_404381: ; CODE XREF: sub_401290+30FA↓j
.text:0000000000404381 xor [rsi], eax
.text:0000000000404383 add rsi, 4
.text:0000000000404387 cmp [rsi-4], eax
.text:000000000040438A jnz short loc_404381
.text:000000000040438C call loc_4012A4
.text:0000000000404391 retn*/

idc脚本(发现后续有一大堆加密,所以只是用这个暂时解密看看后面的函数怎么样)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <idc.idc>

static xor(addr, p)
{
auto i = 0;
for (i = 0; Dword(addr + i - 4) != p; )
{
PatchDword(addr + i, Dword(addr + i) ^ p);
i = i + 4;
}
}

static main()
{
xor(0x004012A4, 0xEA228DE6);
}

loc_4012A4解密后的结果

1
2
3
4
5
6
7
8
.text:00000000004012A4 loc_4012A4:                             ; CODE XREF: sub_401290+30FC↓p
.text:00000000004012A4 ; DATA XREF: sub_401290+30E9↓o
.text:00000000004012A4 mov r15b, [rdi+2Dh] ;flag[0x2d]
.text:00000000004012A8 xor r15b, [rdi+0Eh] ;flag[0x0e]^flag[0x2d]
.text:00000000004012AC cmp r15b, 1Dh ;flag[0x0e]^flag[0x2d]=0x1d,作为flag校验条件
.text:00000000004012B0 jz near ptr qword_404348+8
.text:00000000004012B6 xor eax, eax
.text:00000000004012B8 retn

接下来的结构都是类似于这个的循环,所以上脚本(注意 需要关掉graph模式的comments)

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
import idaapi
import idc
from z3 import *
def simulate_xor(address, xor_value):
# 模拟异或操作
while True:
patch_dword(address, idc.get_wide_dword(address) ^ xor_value)#patch_dword改变对应地址的四字节
if(idc.get_wide_dword(address)==xor_value):
break
address+=4
def check(addr):#获取对应的值的z3条件因为有多种形式 mov就有[rdi],[rdi+1],[rdi+0Eh],这三种情况
#cmp也有 1,1dh这两类情况
#所以情况有点多直接用下面这个复杂的判断式子判断,也可以用一坨if else也行
l=int(str(idc.generate_disasm_line(addr,1)).split('+')[1][:-2],16) if ('+' in str(idc.generate_disasm_line(addr,1))and 'h' in str(idc.generate_disasm_line(addr,1))) else int(str(idc.generate_disasm_line(addr,1)).split('+')[1][:-1],16) if ('h' not in str(idc.generate_disasm_line(addr,1))and '+' in str(idc.generate_disasm_line(addr,1)) ) else 0
r=int(str(idc.generate_disasm_line(addr+4,1)).split('+')[1][:-2],16) if ('+' in str(idc.generate_disasm_line(addr+4,1))and 'h' in str(idc.generate_disasm_line(addr+4,1))) else int(str(idc.generate_disasm_line(addr+4,1)).split('+')[1][:-1],16) if ('h' not in str(idc.generate_disasm_line(addr+4,1))and '+' in str(idc.generate_disasm_line(addr+4,1)) ) else 0
e=int(str(idc.generate_disasm_line(addr+8,1)).split(',')[1].split('h')[0],16)
return f'flag[{l}]^flag[{r}]=={e}'#返回对应的字符串
start=0x404374#最开始的加密地址
nextptr=0x401290#最开始的flag校验
count=0
flag=[BitVec(f'flag[{i}]',8) for i in range(61)]#创建flag变量
f=Solver()#新建一个求解器类
for i,j in enumerate('amateursCTF{'):#添加基础条件前几个字符必须得是"amateursCTF{",最后的字符必须得是"}"
f.add(flag[i]==ord(j))
f.add(flag[60]==ord('}'))
while True:
f.add(eval(check(nextptr)))#添加z3条件,eval执行对应的字符串获得flag[xx]^flag[xx]==xx,因为条件需要的是python中的等式判断所以直接用eval
tmp=str(idc.generate_disasm_line(start,1))
print(tmp)
instruction = int(tmp[len('mov eax, '):-1],16)
tmp=idc.generate_disasm_line(start+24,1)#有的时候需要第二个参数为1,为0可能会获取当前的数据而不是代码
print(tmp)
if '+' not in tmp:#用+号作为分隔符将call near ptr qword_402348+1BCh分成[call near ptr qword_402348,1BCh]这个列表用于得到对应地址值因为每次都只用了6个字符(此处为402348)作为地址所以直接获得第1个元素的后6个字符加上1BC(去掉h)就行
nextptr=int(tmp[-6:],16)
else:
tmplist=tmp.split('+')
if 'h' in tmplist[1]:
nextptr=int(tmplist[0][-6:],16)+int(tmplist[1][:-1],16)
else:
nextptr=int(tmplist[0][-6:],16)+int(tmplist[1],16)
count+=1
simulate_xor(nextptr,instruction)
tmp=str(idc.generate_disasm_line(nextptr+12,1))
print(tmp)
if 'jz' not in tmp:#jz相当于跳转到下一次加密,如果没有jz了就代表加密结束了,所以就结束解密循环
print(count,hex(nextptr+12))
break
if '+' not in tmp:#有些情况是地址+数字代表一个位置,也有直接jz地址的情况
start=int(tmp[-6:],16)#获取后6个字符串并用16进制形式转化成数字
else:
tmplist=tmp.split('+')#用+号作为分隔符将jz near ptr qword_404348+8分成[jz near ptr qword_404348,8]这个列表用于得到对应地址值
if 'h' in tmplist[1]:
start=int(tmplist[0][-6:],16)+int(tmplist[1][:-1],16)#有h代表16进制数同时又有+号所以直接模拟运算获得对应地址
else:
start=int(tmplist[0][-6:],16)+int(tmplist[1],16)#没有h直接获取+号前面6个字符和+号后面所有数值
print(hex(start))
while(f.check()==sat):#check()函数检查是否有解
condition = []
m = f.model()#m用于得到最后的解
p=""
for i in range(61):
p+=chr(int("%s" % (m[flag[i]])))#将61个flag字符添加进flag字符串里
condition.append(flag[i]!=int("%s" % (m[flag[i]])))
print(p)#输出flag
f.add(Or(condition))#将现在的情况添加进条件内防止下次循环获得一样的情况,用于检查是否有多解
  • 标题: headache
  • 作者: runwu2204
  • 创建于 : 2023-08-29 16:59:21
  • 更新于 : 2024-04-12 02:23:51
  • 链接: https://runwu2204.github.io/2023/08/29/CTF WP/Re/headache/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
headache