鹏城杯 2024 初赛 Reverse
minire
有个elf的upx,随便找了个脱壳教程
https://blog.csdn.net/m0_49936845/article/details/119976496
main函数
下面的else是假的,你需要手动传第一个参数是root才会进第一个条件。(顺便提一下,fake的flag是fksdde7039,MD5爆破出的)
然后去real里面,是一个真正让你输入的地方,不过只是输出了input:,真正的逻辑在下面那个函数。
但是核心函数基本无法直接反编译,我们直接看汇编吧。
经典的VM(Handler + Dispatcher)
我们可以找到一个getchar sub_402907然后就可以不断进行硬件断点跟数据了
注意这一块
稍微分析一下
然后我们下断点
进行条件断点,打印值
这样我们就trace了vm
x[0]^=4
x[0]+=12
x[0]^=11
x[0]^=12
x[0]-=2
x[0]-=1
x[0]^=6
x[1]+=10
x[1]^=12
x[1]+=5
x[2]+=4
x[2]+=14
x[3]-=10
x[3]-=3
x[3]+=13
x[3]+=7
x[3]-=9
x[3]^=14
x[4]-=7
x[4]-=15
x[5]^=14
x[6]^=2
x[6]+=0
x[6]^=2
x[6]+=9
x[6]+=3
x[6]-=2
x[6]+=13
x[6]^=1
x[7]+=8
x[7]+=12
x[7]^=15
x[7]+=5
x[7]^=3
x[7]+=8
x[8]+=8
x[8]+=2
x[8]^=8
x[8]-=12
x[8]+=11
x[8]-=3
x[8]^=9
x[8]-=2
x[8]+=8
x[9]-=0
x[9]-=2
x[10]+=11
x[11]-=2
x[11]^=7
x[11]-=1
x[11]-=5
x[12]+=1
x[12]-=13
x[12]+=12
x[13]-=2
x[13]-=14
x[13]-=9
x[13]^=14
x[13]+=1
x[13]+=10
x[13]-=7
x[13]-=1
x[13]+=9
x[14]-=0
x[14]-=11
x[14]^=4
x[14]+=9
x[15]^=14
x[15]+=5
x[15]-=12
x[15]-=4
x[15]-=4
x[15]+=15
x[15]+=14
x[15]-=4
x[15]^=5
x[15]-=12
x[16]^=11
x[16]+=13
x[16]-=11
x[16]^=4
x[16]+=10
x[16]-=8
x[17]+=10
x[17]+=15
x[17]+=2
x[17]-=6
x[17]-=9
x[17]-=9
x[17]+=3
x[17]+=5
x[17]-=15
x[17]+=2
x[18]-=14
x[18]-=13
x[19]^=7
x[19]-=15
x[19]-=1
x[19]^=3
x[19]-=3
x[19]+=11
x[19]+=10
x[19]+=10
x[20]+=8
x[20]-=6
x[20]-=7
x[20]^=4
x[20]-=1
x[20]^=3
x[21]+=5
x[21]+=2
x[21]+=10
x[21]^=0
x[22]-=7
x[22]+=0
x[22]+=8
x[22]+=1
x[22]+=1
x[22]^=10
x[22]-=14
x[22]^=15
x[22]-=7
x[22]^=8
x[23]^=4
x[23]-=9
x[23]+=15
x[24]+=12
x[24]^=2
x[24]^=4
x[24]-=7
x[24]+=1
x[25]+=0
x[25]+=9
x[25]^=2
x[25]+=14
x[25]^=11
x[25]^=1
x[25]+=2
x[25]+=9
x[25]-=9
x[26]+=6
x[27]^=10
x[27]-=4
x[27]-=13
x[27]^=8
x[27]-=14
x[27]+=14
x[27]^=14
x[28]+=1
x[28]^=4
x[28]-=9
x[28]+=9
x[28]-=5
x[28]+=13
x[28]+=1
x[28]^=10
x[29]^=9
x[30]+=14
x[30]+=12
x[30]^=0
x[30]-=0
x[30]-=15
x[30]^=11
x[30]-=11
x[30]+=8
x[31]+=11
x[31]+=6
x[31]+=1
x[31]^=12
x[31]-=6
x[31]-=14
x[32]-=11
x[32]^=13
x[32]+=5
x[32]^=2
x[32]-=2
x[33]+=13
x[33]^=6
x[33]+=15
x[33]^=0
x[33]-=3
x[33]-=12
x[33]-=15
x[33]^=11
x[34]+=9
x[34]+=10
x[34]+=10
x[34]^=10
x[35]-=13
x[36]^=7
x[36]-=0
x[37]-=11
x[37]+=14
x[37]-=8
x[37]+=4
x[37]+=3
x[37]+=13
x[37]^=3
x[37]^=10
在这之后,我们需要寻找enc(标准加密值)
我们在vm_readByte处下断,然后我们就可以看到enc不断浮现在我们眼前。
最后脚本一把梭
from regadgets import *
from z3 import *
from copy import deepcopy
x = [BitVec(f"x{i}", 8) for i in range(38)]
y = deepcopy(x)
x[0]^=4
x[0]+=12
x[0]^=11
x[0]^=12
x[0]-=2
x[0]-=1
x[0]^=6
x[1]+=10
x[1]^=12
x[1]+=5
x[2]+=4
x[2]+=14
x[3]-=10
x[3]-=3
x[3]+=13
x[3]+=7
x[3]-=9
x[3]^=14
x[4]-=7
x[4]-=15
x[5]^=14
x[6]^=2
x[6]+=0
x[6]^=2
x[6]+=9
x[6]+=3
x[6]-=2
x[6]+=13
x[6]^=1
x[7]+=8
x[7]+=12
x[7]^=15
x[7]+=5
x[7]^=3
x[7]+=8
x[8]+=8
x[8]+=2
x[8]^=8
x[8]-=12
x[8]+=11
x[8]-=3
x[8]^=9
x[8]-=2
x[8]+=8
x[9]-=0
x[9]-=2
x[10]+=11
x[11]-=2
x[11]^=7
x[11]-=1
x[11]-=5
x[12]+=1
x[12]-=13
x[12]+=12
x[13]-=2
x[13]-=14
x[13]-=9
x[13]^=14
x[13]+=1
x[13]+=10
x[13]-=7
x[13]-=1
x[13]+=9
x[14]-=0
x[14]-=11
x[14]^=4
x[14]+=9
x[15]^=14
x[15]+=5
x[15]-=12
x[15]-=4
x[15]-=4
x[15]+=15
x[15]+=14
x[15]-=4
x[15]^=5
x[15]-=12
x[16]^=11
x[16]+=13
x[16]-=11
x[16]^=4
x[16]+=10
x[16]-=8
x[17]+=10
x[17]+=15
x[17]+=2
x[17]-=6
x[17]-=9
x[17]-=9
x[17]+=3
x[17]+=5
x[17]-=15
x[17]+=2
x[18]-=14
x[18]-=13
x[19]^=7
x[19]-=15
x[19]-=1
x[19]^=3
x[19]-=3
x[19]+=11
x[19]+=10
x[19]+=10
x[20]+=8
x[20]-=6
x[20]-=7
x[20]^=4
x[20]-=1
x[20]^=3
x[21]+=5
x[21]+=2
x[21]+=10
x[21]^=0
x[22]-=7
x[22]+=0
x[22]+=8
x[22]+=1
x[22]+=1
x[22]^=10
x[22]-=14
x[22]^=15
x[22]-=7
x[22]^=8
x[23]^=4
x[23]-=9
x[23]+=15
x[24]+=12
x[24]^=2
x[24]^=4
x[24]-=7
x[24]+=1
x[25]+=0
x[25]+=9
x[25]^=2
x[25]+=14
x[25]^=11
x[25]^=1
x[25]+=2
x[25]+=9
x[25]-=9
x[26]+=6
x[27]^=10
x[27]-=4
x[27]-=13
x[27]^=8
x[27]-=14
x[27]+=14
x[27]^=14
x[28]+=1
x[28]^=4
x[28]-=9
x[28]+=9
x[28]-=5
x[28]+=13
x[28]+=1
x[28]^=10
x[29]^=9
x[30]+=14
x[30]+=12
x[30]^=0
x[30]-=0
x[30]-=15
x[30]^=11
x[30]-=11
x[30]+=8
x[31]+=11
x[31]+=6
x[31]+=1
x[31]^=12
x[31]-=6
x[31]-=14
x[32]-=11
x[32]^=13
x[32]+=5
x[32]^=2
x[32]-=2
x[33]+=13
x[33]^=6
x[33]+=15
x[33]^=0
x[33]-=3
x[33]-=12
x[33]-=15
x[33]^=11
x[34]+=9
x[34]+=10
x[34]+=10
x[34]^=10
x[35]-=13
x[36]^=7
x[36]-=0
x[37]-=11
x[37]+=14
x[37]-=8
x[37]+=4
x[37]+=3
x[37]+=13
x[37]^=3
x[37]^=10
s = Solver()
enc = [0x60, 0x7F, 0x73, 0x6B, 0x65, 0x4F, 0x80, 0x5D, 0x64, 0x50, 0x64, 0x2C, 0x37, 0x33, 0x5C, 0x74, 0x45, 0x65, 0x55, 0x60, 0x4A, 0x84, 0x6E, 0x50, 0x7F, 0x58, 0x5F, 0x27, 0x7F, 0x5A, 0x8C, 0x6B, 0x62, 0x60, 0x59, 0x59, 0x45, 0x85]
for i in range(len(x)):
s.add(enc[i] == x[i])
for m in z3_get_models(s):
print(m)
for i in y:
print(chr(m[i].as_long()), end='')
print('')
# flag{AjJIRY77BbuNgpPSswNwCY8gSyawo6fB}
joyVBS
纯送分题
https://www.onlinegdb.com/online_vb_compiler
print出来代码
assembly 'a, Version=0.0, Culture=neutral, PublicKeyToken=null' saved successfully to '/home/a.out'.
Compilation successful
Compilation took 00:00:00.7942080
MsgBox "VBScript, often abbreviated as VBS, is an event-driven programming language developed by Microsoft, primarily used for scripting in the Windows environment."
MsgBox "It is based on the Visual Basic programming language and is designed to be simple and easy to use, especially for those familiar with the BASIC programming language."
MsgBox "And for me, it is the first programming language that I've leart"
MsgBox "Hackers! Have fun with this VBS challenge!"
flag = InputBox("Enter the FLAG:", "Hack for fun")
wefbuwiue = "NalvN3hKExBtALBtInPtNHTnKJ80L3JtqxTboRA/MbF3LnT0L2zHL2SlqnPtJLAnFbIlL2SnFT8lpzFzA2JHrRTiNmT9"
qwfe = 9+2+2+1
Function Base64Decode(base64EncodedString)
Dim xml, elem
Set xml = CreateObject("MSXML2.DOMDocument")
Set elem = xml.createElement("tmp")
elem.dataType = "bin.base64"
elem.text = base64EncodedString
Dim stream
Set stream = CreateObject("ADODB.Stream")
stream.Type = 1 'Binary
stream.Open
stream.Write elem.nodeTypedValue
stream.Position = 0
stream.Type = 2 'Text
stream.Charset = "utf-8"
Base64Decode = stream.ReadText
stream.Close
End Function
Function Caesar(str,offset)
Dim length,char,i
Caesar = ""
length = Len(str)
For i = 1 To length
char = Mid(str,i,1)
If char >= "A" And char <= "Z" Then
char = Asc("A") + (Asc(char) - Asc("A") + offset) Mod 26
Caesar = Caesar & Chr(char)
ElseIf char >= "a" And char <= "z" Then
char = Asc("a") + (Asc(char) - Asc("a") + offset) Mod 26
Caesar = Caesar & Chr(char)
Else
Caesar = Caesar & char
End If
Next
End Function
If flag = Base64Decode(Caesar(wefbuwiue, 26-qwfe)) Then
MsgBox "Congratulations! Correct FLAG!"
Else
MsgBox "Wrong flag."
End If
凯撒加密+base64flag{VB3_1s_S0_e1sY_4_u_r1gh3?btw_1t_iS_a1s0_Us3Fu1_a3D_1nTe3eSt1ng!}
exec
Python Function Hook
import base64
hook_exec = exec
def my_exec(x):
print('myexec', x)
hook_exec(x)
exec = my_exec
...
这样就能拿到每次exec的代码,从而得到
a=True
len=len
G=list
g=range
s=next
R=bytes
o=input
Y=print
def l(S):
i=0
j=0
while a:
i=(i+1)%256
j=(j+S[i])%256
S[i],S[j]=S[j],S[i]
K=S[(S[i]+S[j])%256]
yield K
def N(key,O):
I=len(key)
S=G(g(256))
j=0
for i in g(256):
j=(j+S[i]+key[i%I])%256
S[i],S[j]=S[j],S[i]
z=l(S)
n=[]
for k in O:
n.append(s(z)+2)
return bytes(n).hex()
def E(s,parts_num):
Q=len(s.decode())
S=Q//parts_num
u=Q%parts_num
W=[]
j=0
for i in g(parts_num):
T=j+S
if u>0:
T+=1
u-=1
W.append(s[j:T])
j=T
return W
if __name__=='__main__':
L=o('input the flag: >>> ').encode()
assert len(L)%2==0,'flag length should be even'
t=b'v3ry_s3cr3t_p@ssw0rd'
O=E(L,2)
U=[]
for i in O:
U.append(N(t,i))
print(U)
if U==['1796972c348bc4fe7a1930b833ff10a80ab281627731ab705dacacfef2e2804d74ab6bc19f60','2ea999141a8cc9e47975269340c177c726a8aa732953a66a6af183bcd9cec8464a']:
Y('Congratulations! You got the flag!')
else:
Y('Wrong flag!')
无法直接运行是因为U的第二个少了个',补上就行,上面代码我稍微改了一下,输出魔改rc4的keystream
然后直接解就行flag{thEn_I_Ca5_BE_YoUR_Onl7_ExeCUti6n_So_Use_m3_t0_R0n_tH17_Ex3Cuti0n}
RE5
异常处理改了tea的key,每一轮的sum值是由srand(0),每次取随机数相加得到的,然后直接解标准tea即可。
from regadgets import *
from typing import List, Tuple, Union
from ctypes import c_uint32
rng = WindowsRand(0)
def tea_decrypt_plus(
src: Union[Tuple[int, int], bytes, List[int]], key: Union[List[int], bytes], delta: int = 0x9E3779B9, rounds: int = 32
) -> Union[Tuple[int, int], bytes, List[int]]:
if type(src) == bytes:
result = b''
for i in pack_dword(byte2dword(src)):
result += dword2byte(tea_decrypt_plus(i, key, delta, rounds))
return result
elif type(src) == list:
result = b''
for i in pack_dword(src):
result += dword2byte(tea_decrypt_plus(i, key, delta, rounds))
return result
elif type(src) != tuple:
raise "wrong src type"
# For bytes key
_rands = rng.rands(32)
if type(key) == bytes:
key = byte2dword(key)
elif type(key) != list:
raise "wrong key type"
l, r = c_uint32(src[0]), c_uint32(src[1])
_sum = c_uint32(sum(_rands))
k = [c_uint32(i) for i in key]
for i in range(rounds):
r.value -= (
((l.value << 4) + k[2].value)
^ (l.value + _sum.value)
^ ((l.value >> 5) + k[3].value)
)
l.value -= (
((r.value << 4) + k[0].value)
^ (r.value + _sum.value)
^ ((r.value >> 5) + k[1].value)
)
_sum.value -= _rands[rounds - i - 1]
return (l.value, r.value)
enc = b"\xF8\x63\x20\xEA\x52\xF2\x66\x8F\xEF\x72\x2A\x90\x74\xDA\x1F\x41\x4D\x0D\x59\x19\x17\x43\xE7\xCA\x3F\x0F\x87\x63\x61\xAE\x53\xD7\x22\x3D\x43\x4D\xDD\xC2\xBC\xB2\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x2F\x00\x00\x00\x01\x00\x00\x00\x66\x6C\x61\x67\x7B\x31\x32\x33\x31\x34\x31\x32\x33\x34\x31\x32\x34\x31\x32\x34\x32\x31\x33\x34\x31\x32\x33\x34\x31\x32\x33\x31\x32\x34\x33\x32\x33\x7D\x00\x00\x39\xDC\xE1\x2E\xF1\xEA\xD6\x40\x9A\x76\x29\x5E\x17\xB6\xF4\x38\xD0\x14\xF5\x8C\xA2\x7E\xEB\xBE\xD5\xFA\xBD\x18\xBF\x05\x97\x49"
# enc = b"\x9A\x76\x29\x5E\x17\xB6\xF4\x38"
# enc = b"\x39\xDC\xE1\x2E\xF1\xEA\xD6\x40"
enc = byte2dword(enc)
# print(tea_decrypt(enc, key=[2,2,3,4]))
print(tea_decrypt_plus(enc, key=[2,2,3,3]))
flag{d555ce75ec293c8ed232d83dffb0ff82}
三血
BigTea
唯一的一血,全程黑箱trace+猜的,控制流混淆太恶心了
Xor Trace,有小数字(0~3的xor),对应xxtea的key[(p & 3) ^ e] ^ z,所以直接认为是xxtea
1287150135 = 858927483 ^ 2139777868
3 = 3 ^ 0
3083858674 = 2100375857 ^ 3405693891
3475065957 = 3435709932 ^ 65636745
3581236543 = 3541242640 ^ 107365935
2686381197 = 76041513 ^ 2761335204
1270372984 = 875704628 ^ 2139777868
2 = 3 ^ 1
.....
2296010693 = 1761772981 ^ 3789083248
3006293146 = 1572571602 ^ 4002147656
0 = 2 ^ 2
1724993655 = 425409339 ^ 2139777868
1982135953 = 1995319112 ^ 13294041
2651112074 = 2511582128 ^ 196571450
237532170 = 436319505 ^ 338280731
3939128127 = 71417463 ^ 4002147656
1 = 2 ^ 3
2522458527 = 141479974 ^ 2654435769
289771357 = 285669852 ^ 4421249
2254779822 = 2263679584 ^ 8927182
378091989 = 2166619358 ^ 2544551179
我们慢慢分析,XXTEA的key,可以从Trace里面盯出来,前面异或算出来的是索引值,所以我们很容易就能拿到四个key,然后我们输入11112222作为待加密数据,由于xxtea的特性,z==y,那么我们很方便的可以看到下面四个值在trace中的样子,然后可以倒推出delta = 0x70D7D4CC
Ly = y.value << 2
Ry = y.value >> 3
Lz = z.value << 4
Rz = z.value >> 5
keys
0x7f8a6b4c
0x9e3779b9
0xdeafbeef
0xcafec3c3
然后,我们可以使用自己的xxtea加密数据,发现我们的加密数据只在trace的中间,而不是最后,说明加密可能不只一轮或者是xxtea魔改轮数,经过测试是后者,通过fuzz,我们可以知道魔改是 6 + 52//n -> 32 + 52 // n,我们抄出来就行了,最后至于找enc,是通过在最后一轮对于密文下断,然后在程序内存中搜索第一个加密的,进行硬件断点,然后跑,就可以找到cmp。
然后
我们可以观察到下面对cl进行的操作,cl如果不为0则两段密文不一样,我们每次运行打断点手动置0,然后输入很长的input,就可以拿到所有的对比值
cmp 673017408, 1214171175
cmp 3365566770, 3066933611
cmp 1583712898, 4062421290
cmp 1719532837, 3922368852
cmp 2253378100, 2540993788
cmp 1994934834, 1332676358
cmp 4110442914, 22469667
cmp 4185531028, 3633624522
cmp 1279845426, 2241914041
cmp 1481920662, 3542748258
cmp 725179328, 1546291565
最后,使用regadgets库(由于还在开发,小范围测试,所以把库内的代码都复制了出来)进行解密。即可。
from typing import List, Union
from ctypes import c_uint32
def xxtea_std_shift(z, y, sum, k, p, debug = False):
if debug:
print("-----------------XXTEA_STD_SHIFT-----------------")
e = (sum.value >> 2) & 3
PE = (p & 3) ^ e
Ly = y.value << 2
Ry = y.value >> 3
Lz = z.value << 4
Rz = z.value >> 5
LzRy = Rz ^ Ly
LyRz = Ry ^ Lz
SY = sum.value ^ y.value
K = k[PE].value
KZ = K ^ z.value
result = (LzRy + LyRz) ^ (KZ + SY)
if debug:
print("sum = ", hex(sum.value))
print("e = ", hex(e))
print("PE = ", hex(PE))
print("SY = ", hex(SY))
print("z = ", hex(z.value))
print("y = ", hex(y.value))
print("Ly = ", hex(Ly))
print("Ry = ", hex(Ry))
print("Lz = ", hex(Lz))
print("Rz = ", hex(Rz))
print("LzRy= ", hex(LzRy & 0xffffffff))
print("LyRz= ", hex(LyRz & 0xffffffff))
print("K = ", hex(K))
print("KZ = ", hex(KZ))
print("ret = ", hex(result & 0xffffffff))
print("-----------------XXTEA_STD_SHIFT-----------------")
return result
def xxtea_decrypt(
src: Union[List[int], bytes],
key: List[int],
delta: int = 0x9E3779B9,
round_base: int = 6,
round_addi: int = 52,
shift_func=xxtea_std_shift,
debug=False,
) -> Union[List[int], bytes]:
in_bytes = False
if type(src) == bytes:
src = byte2dword(src)
in_bytes = True
if type(key) == bytes:
key = byte2dword(key)
if in_bytes:
return dword2byte(xxtea_decrypt(src, key, delta, round_base, round_addi, shift_func, debug))
src = [c_uint32(i) for i in src]
key = [c_uint32(i) for i in key]
sum, e, y = c_uint32(0), c_uint32(0), c_uint32(0)
delta = c_uint32(delta)
n = len(src)
rounds = round_base + round_addi // n
sum.value = rounds * delta.value
y = src[0]
for _ in range(rounds):
for p in range(n - 1, 0, -1):
z = src[p - 1]
shift_result = shift_func(z, y, sum, key, p, debug)
src[p].value -= shift_result
y = src[p]
p -= 1
z = src[n - 1]
shift_result = shift_func(z, y, sum, key, p, debug)
src[0].value -= shift_result
y = src[0]
sum.value -= delta.value
return [i.value for i in src]
enc = [1214171175,3066933611,4062421290,3922368852,2540993788,1332676358,22469667,3633624522,2241914041,3542748258,1546291565]
enc = xxtea_decrypt(enc, key=[0x7f8a6b4c, 0x9e3779b9, 0xdeafbeef, 0xcafec3c3],delta=0x7f8a6b4c, round_base=32)
print(dword2byte(enc))
# b'flag{9aad3962-8ad5-48d0-83dc-1e60a81439a1}\x00\x00'
Rafflesia
动态调试,有反调试,直接用ScyllaHide过掉。
base64换表,base64稍微改了一下:最后xor了0x18
最后在memcmp下断拿密文就行
FlvtterM
Blutter逆向
然后就没时间做了,看到个换表base58,前面有一个x[i+1] ^= x[i]和一个换位置,但是最前面还有一个SBox相关的操作,SBox这个没看出来是什么。