常年不去上早八课的人,今天居然在八点前就醒了,要问为什么?这比赛从早上八点打到晚上八点>...<(悲)
不过,咱们队伍在下午就把逆向题ak了~居然没有坐牢到晚上(●'◡'●)
动态调试,题目里有个inline hook,在这里打个断点
那么网址就是Just_An_APIH00k11.com
die查一下壳
有sleep反调试,把sleep nop掉
这里读取了名称为cod的资源,用resource hacker
把资源复制下来
然后向下执行,这里是一个对cod资源进行解密的地方
这里要注意的是如果检测到调试器,那么byte_7FF6DA64F000[3]
将会被赋值为36
所以要把这个if语句通过修改ZF标志位的方式来绕过反调试
cod资源解密脚本如下
1
2
3
4
5
6
7
8
|
arr
=
[
0x18
,
0x57
,
0x68
,
0x64
]
with
open
(
'COD101.bin'
,
'rb'
) as f:
b
=
f.read()
b
=
bytearray(b)
for
i
in
range
(
len
(b)):
b[i]
=
b[i] ^ arr[i
%
4
]
with
open
(
'COD_de.bin'
,
'wb'
) as f:
f.write(b)
|
用ida打开,看到有花指令
nop一下,主要的改动有这几处
于是得到如下的伪代码
看算法是魔改的RC4,exp如下
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
|
class
RC4:
def
__init__(
self
, key)
-
>
None
:
self
.key
=
key
self
.S
=
0
self
.__rc4_init__()
def
__rc4_init__(
self
):
S
=
[i
for
i
in
range
(
256
)]
j
=
0
for
i
in
range
(
256
):
j
=
(
2
*
j
+
S[i]
+
key[i
%
len
(key)])
%
256
S[i], S[j]
=
S[j], S[i]
self
.S
=
S
def
rc4_encrypt(
self
, plain)
-
>
list
:
i
=
0
j
=
0
cipher
=
[]
cnt
=
0
for
p
in
plain:
p
=
(p
+
256
-
cnt
%
0xd
)
%
256
cnt
+
=
1
i
=
(i
+
j)
%
256
j
=
(j
+
self
.S[i])
%
256
self
.S[i],
self
.S[j]
=
self
.S[j],
self
.S[i]
tmp
=
self
.S[(
self
.S[i]
+
self
.S[j]
+
j)
%
256
]
k
=
p ^ tmp
cipher.append(k)
return
cipher
key
=
[
0x5D
,
0x42
,
0x62
,
0x29
,
0x3
,
0x36
,
0x47
,
0x41
,
0x15
,
0x36
]
data
=
[
0xF7
,
0x2E
,
0x34
,
0xF0
,
0x72
,
0xCF
,
0x5E
,
0x0A
,
0xBB
,
0xEC
,
0xB1
,
0x2B
,
0x70
,
0x88
,
0x88
,
0xED
,
0x46
,
0x38
,
0xDB
,
0xDA
,
0x6C
,
0xBD
,
0xD4
,
0x06
,
0x77
,
0xF2
,
0xCF
,
0x56
,
0x88
,
0xC6
,
0x31
,
0xD2
,
0xB7
,
0x5A
,
0xC1
,
0x42
,
0xB0
,
0xF4
,
0x48
,
0x37
,
0xF5
,
0x2C
,
0xF5
,
0x58
]
rc4
=
RC4(key)
plain
=
rc4.rc4_encrypt(data)
print
(''.join(
map
(
chr
,plain)))
|
查个壳,是python逆向
用pyinstxtractor
脱一下
用看一下ez_py.pyc的源代码
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
|
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.11
import
ctypes
from
time
import
*
from
ctypes
import
*
from
ctypes
import
wintypes
from
hashlib
import
md5
class
_STARTUPINFO(Structure):
_fields_
=
[
(
'cb'
, c_ulong),
(
'lpReserved'
, c_char_p),
(
'lpDesktop'
, c_char_p),
(
'lpTitle'
, c_char_p),
(
'dwX'
, c_ulong),
(
'dwY'
, c_ulong),
(
'dwXSize'
, c_ulong),
(
'dwYSize'
, c_ulong),
(
'dwXCountChars'
, c_ulong),
(
'dwYCountChars'
, c_ulong),
(
'dwFillAttribute'
, c_ulong),
(
'dwFlags'
, c_ulong),
(
'wShowWindow'
, c_ushort),
(
'cbReserved2'
, c_ushort),
(
'lpReserved2'
, c_char_p),
(
'hStdInput'
, c_ulong),
(
'hStdOutput'
, c_ulong),
(
'hStdError'
, c_ulong)]
class
_PROCESS_INFORMATION(Structure):
_fields_
=
[
(
'hProcess'
, c_void_p),
(
'hThread'
, c_void_p),
(
'dwProcessId'
, c_ulong),
(
'dwThreadId'
, c_ulong)]
StartupInfo
=
_STARTUPINFO()
ProcessInfo
=
_PROCESS_INFORMATION()
key1
=
bytes(md5(b
'bin1bin1bin1'
).hexdigest().encode())
file
=
open
(
'bin1'
,
'rb'
).read()
arr
=
range
(
len
(
file
))()
open
(
'bin1'
,
'wb'
).write(bytes(arr))
sleep(
0
)
bet
=
ctypes.windll.kernel32.CreateProcessA(b
'bin1'
, ctypes.c_int(
0
), ctypes.c_int(
0
), ctypes.c_int(
0
), ctypes.c_int(
0
), ctypes.c_int(
0
), ctypes.c_int(
0
), ctypes.c_int(
0
), byref(StartupInfo), byref(ProcessInfo))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ProcessInfo.hProcess), ctypes.c_int(
-
1
))
open
(
'bin1'
,
'wb'
).write(
file
)
|
用ida反编译bin1失败,看来是被加密了
用这个代码看一下字节码
1
2
3
4
5
6
7
|
import
marshal, dis
f
=
open
(
"ez_py.pyc"
,
"rb"
).read()
code
=
marshal.loads(f[
16
:])
#这边从16位开始取因为是python3 python2从8位开始取
dis.dis(code)
|
在最后面得到了这个
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
Disassembly of <code
object
<listcomp> at
0x00000297CC7F8E70
,
file
"ez_py.py"
, line
59
>:
59
0
RESUME
0
2
BUILD_LIST
0
4
LOAD_FAST
0
(.
0
)
>>
6
FOR_ITER
50
(to
108
)
8
STORE_FAST
1
(i)
10
LOAD_GLOBAL
0
(key1)
22
LOAD_FAST
1
(i)
24
LOAD_GLOBAL
3
(NULL
+
len
)
36
LOAD_GLOBAL
0
(key1)
48
PRECALL
1
52
CALL
1
62
BINARY_OP
6
(
%
)
66
BINARY_SUBSCR
76
LOAD_GLOBAL
4
(
file
)
88
LOAD_FAST
1
(i)
90
BINARY_SUBSCR
100
BINARY_OP
12
(^)
104
LIST_APPEND
2
106
JUMP_BACKWARD
51
(to
6
)
>>
108
RETURN_VALUE
|
那么解密代码如下
1
2
3
4
5
6
7
8
9
10
11
|
from
hashlib
import
md5
key1
=
bytes(md5(b
'bin1bin1bin1'
).hexdigest().encode())
# print(key1)
file
=
open
(
'bin1'
,
'rb'
).read()
arr
=
[key1[i
%
len
(key1)] ^
file
[i]
for
i
in
range
(
len
(
file
))]
# open('bin1', 'wb').write(bytes(arr))
with
open
(
'bin1__'
,
'wb'
) as f:
f.write(bytes(arr))
|
反编译出来是这个
1
2
3
4
5
6
7
8
9
10
11
|
from
hashlib
import
md5
key1
=
bytes(md5(b
'bin2bin2bin2'
).hexdigest().encode())
# print(key1)
file
=
open
(
'bin2'
,
'rb'
).read()
arr
=
[key1[i
%
len
(key1)] ^
file
[i]
for
i
in
range
(
len
(
file
))]
# open('bin1', 'wb').write(bytes(arr))
with
open
(
'bin2__'
,
'wb'
) as f:
f.write(bytes(arr))
|
然后用ida反编译bin2__
那么这就是正常的逆向题了
btea
函数里面是这个,这是一个xxtea算法
写一下exp
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
|
#include <iostream>
#include <stdio.h>
using namespace std;
#include <stdint.h>
#define DELTA 0x7937B99E
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
void btea(uint32_t
*
v,
int
n, uint32_t const key[
4
]) {
uint32_t y, z,
sum
;
unsigned p, rounds, e;
if
(n >
1
) {
/
*
Coding Part
*
/
rounds
=
/
*
6
+
*
/
52
/
n;
sum
=
0
;
z
=
v[n
-
1
];
do {
sum
+
=
DELTA;
e
=
(
sum
>>
2
) &
3
;
for
(p
=
0
; p < n
-
1
; p
+
+
) {
y
=
v[p
+
1
];
z
=
v[p]
+
=
MX;
}
y
=
v[
0
];
z
=
v[n
-
1
]
+
=
MX;
}
while
(
-
-
rounds);
}
else
if
(n <
-
1
) {
/
*
Decoding Part
*
/
n
=
-
n;
rounds
=
/
*
6
+
*
/
52
/
n;
sum
=
rounds
*
DELTA;
y
=
v[
0
];
do {
e
=
(
sum
>>
2
) &
3
;
for
(p
=
n
-
1
; p >
0
; p
-
-
) {
z
=
v[p
-
1
];
y
=
v[p]
-
=
MX;
}
z
=
v[n
-
1
];
y
=
v[
0
]
-
=
MX;
}
while
((
sum
-
=
DELTA) !
=
0
);
}
}
int
main()
{
uint32_t const key[
4
]
=
{
0x4B5F
,
0xDEAD
,
0x11ED
,
0xB3CC
};
uint32_t data[
11
]
=
{
0xCC45699D
,
0x683D5352
,
0xB8BB71A0
,
0xD3817AD
,
0x7547E79E
,
0x4BDD8C7C
,
0x95E25A81
,
0xC4525103
,
0x7049B46F
,
0x5417F77C
,
0x65567138
};
uint32_t
*
sent
=
data;
/
/
btea(sent,
11
, key);
/
/
printf(
"coded:%x %x\n"
, sent[
0
], sent[
1
]);
btea(sent,
-
11
, key);
/
/
printf(
"decoded:%x %x\n"
, sent[
0
], sent[
1
]);
for
(
int
i
=
0
; i <
11
; i
+
+
) {
for
(
int
j
=
0
; j <
4
; j
+
+
)
{
printf(
"%c"
, sent[i] &
0xff
);
sent[i] >>
=
8
;
}
}
return
0
;
}
/
/
DASCTF{
7eb20cb2
-
deac
-
11ed
-
ae42
-
94085339ce84
}
|
在这个地方动调
可以发现数组的下标在0~12之间循环
我们随便打开一个BMP类型的文件,用010看看
对于BMP类型的文件前两个字节必定是43 4D
既然这个加密的bmp的每一个字节进行的都是异或,那我们可以将前两个字节异或看看
n和c是密钥enc_by_dasctf
的第2个和第3个字符,按照这个序列,我们向后将密钥向后延申看看后面的情况如何
所以我们写个脚本,从密钥的第二位开始,循环异或
1
2
3
4
5
6
7
|
key
=
"enc_by_dasctf"
with
open
(
'cap.bin'
,
'rb'
) as f:
s
=
bytearray(f.read())
for
i
in
range
(
len
(s)):
s[i] ^
=
ord
(key[(i
+
1
)
%
len
(key)])
with
open
(
'flag.bmp'
,
'wb'
) as f:
f.write(s)
|
得到flag
查一下壳,是go逆向
用这个脚本恢复一下go符号https://github.com/renshareck/IDAGolangHelper_SupportGo1.20
依次点击如下按钮
首先判断key正确与否,看来这是个rsa
用yafu解一下p和q
然后解出密钥
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
gmpy2
from
Crypto.Util.number
import
long_to_bytes
n
=
0x1d884d54d21694ccd120f145c8344b729b301e782c69a8f3073325b9c5
p
=
37636318457745167234140808130156739
q
=
21154904887215748949280410616478423
c
=
0xfad53ce897d2c26f8cad910417fbdd1f0f9a18f6c1748faca10299dc8
e
=
0x10001
phi
=
(p
-
1
)
*
(q
-
1
)
d
=
gmpy2.invert(e, phi)
m
=
pow
(c, d, n)
print
(long_to_bytes(m))
# E@sy_RSA_enc7ypt
|
再往后看,
动调了一下看到iv和key都是一样的
所以直接写个exp把加密的文件解密
1
2
3
4
5
6
7
8
9
10
|
from
Crypto.Cipher
import
AES
password
=
b
'E@sy_RSA_enc7ypt'
# 秘钥必须为 16 字节或者 16 字节的倍数的字节型数据
iv
=
b
'E@sy_RSA_enc7ypt'
# iv 偏移量,bytes 类型
with
open
(
'encrypted.bin'
,
'rb'
) as f:
en_text
=
f.read()
aes
=
AES.new(password, AES.MODE_CBC, iv)
# CBC 模式下解密需要重新创建一个 aes 对象
de_text
=
aes.decrypt(en_text)
with
open
(
'decrypt.exe'
,
'wb'
) as f:
f.write(de_text)
|
运行一下解密出的exe,就得到flag了
更多【DASCTF 2023六月挑战赛 二进制专项 RE writeup】相关视频教程:www.yxfzedu.com