比赛难度中等偏难,主办方修了非预期还没修好,不修了摆烂了。
队伍成绩:425
pt/10
kill/全国48
名/华中赛区3
名
个人成绩:359
pt/6
kill/1
三血/全国42
名
Misc
ez_usb[三血]
打开流量包,发现好多USB流量。
其中2.8
可以确定是键盘。
2.4
查了下好像是U盘。
2.10
有个罗技标,查了下产品id应该也是键盘。
所以目标放在那俩把键盘上,写一下带过滤的提取,分别提取出两个键盘的流量数据。
tshark -r "ez_usb.pcapng" -Y "usb.src==\"2.8.1\" && usb.dst==host" -T fields -e usbhid.data > keyboarda.txt
tshark -r "ez_usb.pcapng" -Y "usb.src==\"2.10.1\" && usb.dst==host" -T fields -e usbhid.data > keyboardb.txt
改改通用的键盘流量解码脚本,跑一下。
mappings_a = {0x04: "a", 0x05: "b", 0x06: "c", 0x07: "d", 0x08: "e", 0x09: "f", 0x0a: "g", 0x0b: "h", 0x0c: "i",
0x0d: "j", 0x0e: "k", 0x0f: "l", 0x10: "m", 0x11: "n", 0x12: "o", 0x13: "p", 0x14: "q", 0x15: "r",
0x16: "s", 0x17: "t", 0x18: "u", 0x19: "v", 0x1a: "w", 0x1b: "x", 0x1c: "y", 0x1d: "z", 0x1e: "1",
0x1f: "2", 0x20: "3", 0x21: "4", 0x22: "5", 0x23: "6", 0x24: "7", 0x25: "8", 0x26: "9", 0x27: "0",
0x28: "[enter]", 0x2a: "[del]", 0x2b: "[tab]", 0x2c: " ", 0x2d: "-", 0x2e: "=", 0x2f: "[", 0x30: "]", 0x31: "\\",
0x32: "~", 0x33: ";", 0x34: "'", 0x36: ",", 0x37: ".", 0x39: "[cap]"}
mappings_A = {0X04: "A", 0X05: "B", 0X06: "C", 0X07: "D", 0X08: "E", 0X09: "F", 0X0A: "G", 0X0B: "H", 0X0C: "I",
0X0D: "J", 0X0E: "K", 0X0F: "L", 0X10: "M", 0X11: "N", 0X12: "O", 0X13: "P", 0X14: "Q", 0X15: "R",
0X16: "S", 0X17: "T", 0X18: "U", 0X19: "V", 0X1A: "W", 0X1B: "X", 0X1C: "Y", 0X1D: "Z", 0X1E: "1",
0X1F: "2", 0X20: "3", 0X21: "4", 0X22: "5", 0X23: "6", 0X24: "7", 0X25: "8", 0X26: "9", 0X27: "0",
0X28: "[ENTER]", 0X2A: "[DEL]", 0X2B: "[TAB]", 0X2C: " ", 0X2D: "-", 0X2E: "=", 0X2F: "[", 0X30: "]", 0X31: "\\",
0X32: "~", 0X33: ";", 0X34: "'", 0X36: ",", 0X37: ".", 0X39: "[CAP]"}
data = ""
with open("keyboarda.txt", 'r') as f:
for i in f:
a = bytes.fromhex(i)
if a[0] == 0:
if a[2] != 0:
data += mappings_a[a[2]]
elif a[0] == 0x20:
if a[2] != 0:
data += mappings_A[a[2]]
print(data)
data = ""
with open("keyboardb.txt", 'r') as f:
for i in f:
a = bytes.fromhex(i)
if a[0] == 0:
if a[2] != 0:
data += mappings_a[a[2]]
elif a[0] == 0x20:
if a[2] != 0:
data += mappings_A[a[2]]
print(data)
发现一堆十六进制和Tab
、CapsLock
、Delete
,反正大小写对十六进制不影响,所以忽略掉CapsLock
,然后Tab
键感觉也没啥用,反正就在开头有一次键入,至于Delete
的话这里意思应该是删掉前一个键入,所以最终得到的两串十六进制是:
526172211a0700cf907300000d00000000000000c4527424943500300000002a00000002b9f9b0530778b5541d33080020000000666c61672e747874b9ba013242f3afc000b092c229d6e994167c05a78708b271ffc042ae3d251e65536f9ada87c77406b67d0e6316684766a86e844dc81aa2c72c71348d10c43d7b00400700
35c535765e50074a
前者把十六进制转raw得到一个rar
压缩包。
打开发现需要密码。
尝试用第二个字符串当密码成功解压,得到flag。
flag{20de17cc-d2c1-4b61-bebd-41159ed7172d}
问卷调查
掐点去做的问卷,没赶上后面出问题的时候,直接出了flag交了。
flag{Thanksforplayingourgames}
Crypto
基于挑战码的双向认证+基于挑战码的双向认证2
非预期了,直接扫盘就能找到两题的flag。
find / -name '*flag*'
flag{81f14d6d-aef0-418b-85cb-0e8d4fc9515a}
flag{34f5fdaf-c373-47fd-afab-01ed2914c11a}
Reverse
baby_tree
拿到题目,发现是个swift
的ast
,没找到啥好用的一键还原成.swift
代码的工具,那就强行转成自己擅长的python
代码再逆向吧。
先当作js高亮一下,方便看代码关系和层次。
然后手动标记一下一些重要的关键词,主要如下:
encoded
keyValue
extension
@
i@
b@
k@
value=
r0
r1
r2
r3
然后就来分析ast
了。
先是定义了个check
函数,传参是俩字符串,然后返回逻辑值。根据缩进判断函数主体逻辑在brace_stmt
块里。
然后是定义了个b
变量,初始值是拆分encoded
得到的。
然后定义的k
变量和b
差不多,只不过是拆分keyValue
的。
然后定义了几个未初始化的变量r0
、r1
、r2
、r3
。
然后下面就是一个超大循环体和超大返回语句。
先看for
循环,迭代器i
的定义比较长,...
操作符类似于kotlin
的..
,就是个range
,然后下面俩argument
分别表示开头和结尾,所以开头很简单,是0
,结尾是外面一个-
操作符,两个元是b.count
和4
,所以现在这个for
循环就是for i in range(0, len(b) - 4+1):
,+1
是因为python
里结尾不包括,但是swift
是包括的。
然后看for
循环主体部分,主要就是六个感觉像赋值语句的东西。
先看第一个,赋值的目标是一个四元组,内容是r0
、r1
、r2
、r3
。
然后值的话,第一个就是b[i]
,第二个是b[i+1]
,第三个是b[i+2]
,第四个是b[i+3]
。
所以python
代码就是r0, r1, r2, r3 = b[i], b[i+1], b[i+2], b[i+3]
。
然后看第二个复制块,赋值目标是b[i+0]
。
值是r2 ^ ((k[0] + (r0 >> 4)) & 0xff)
,一层一层分析操作符及其元就可以得出,不再详细解释。
下面4个赋值块和上面这2个赋值块其实代码也差不多,所以不过多解释,分别是b[i+1] = r3 ^ ((k[1] + (r1 >> 2)) & 0xff)
、b[i+2] = r0 ^ k[2]
、b[i+3] = r1 ^ k[3]
、k[0], k[1], k[2], k[3] = k[1], k[2], k[3], k[0]
最后来看函数return
,就是比较b
和一个数组常量。
然后看函数外面的顶层代码。先是判断了下cmd
传参个数,确保>=2
。
然后把arg[1]
赋值给data
。
key
定义成常量,值是345y
。
然后最后调用上面的check
函数,传参是(data, key)
,把结果赋值给result
。
所以最后代码就是:
def check(encoded: str, keyValue: str) -> bool:
b = [ord(i) for i in encoded] # len == 42
k = [ord(i) for i in keyValue] # len == 4
r0, r1, r2, r3 = 0, 0, 0, 0
for i in range(0, len(b) - 4+1):
r0, r1, r2, r3 = b[i], b[i+1], b[i+2], b[i+3]
b[i+0] = r2 ^ ((k[0] + (r0 >> 4)) & 0xff)
b[i+1] = r3 ^ ((k[1] + (r1 >> 2)) & 0xff)
b[i+2] = r0 ^ k[2]
b[i+3] = r1 ^ k[3]
k[0], k[1], k[2], k[3] = k[1], k[2], k[3], k[0]
return b == [88, 35, 88, 225, 7, 201, 57, 94, 77, 56, 75, 168, 72, 218, 64, 91, 16, 101, 32, 207, 73, 130, 74, 128, 76, 201, 16, 248, 41, 205, 103, 84, 91, 99, 79, 202, 22, 131, 63, 255, 20, 16]
if __name__ == '__main__':
print(check(flag, "345y"))
那么这就简单了,其中涉及的其实主要就是异或操作,反向异或就完事,然后注意一下赋值的顺序问题,以及key
的初始值应该先走一轮,就可以直接写个逆向逻辑的函数去解了:
def rev(encoded: list, keyValue: str) -> str:
b = encoded
k = [ord(i) for i in keyValue] # len == 4
for i in range(0, len(b) - 4+1):
k[0], k[1], k[2], k[3] = k[1], k[2], k[3], k[0]
for i in range(len(b) - 4+1-1, 0-1, -1):
k[1], k[2], k[3], k[0] = k[0], k[1], k[2], k[3]
r1 = b[i + 3] ^ k[3]
r0 = b[i + 2] ^ k[2]
r3 = b[i + 1] ^ ((k[1] + (r1 >> 2)) & 0xff)
r2 = b[i + 0] ^ ((k[0] + (r0 >> 4)) & 0xff)
(b[i], b[i + 1], b[i + 2], b[i + 3]) = (r0, r1, r2, r3)
return bytearray(b).decode()
if __name__ == '__main__':
print(rev(
[88, 35, 88, 225, 7, 201, 57, 94, 77, 56, 75, 168, 72, 218, 64, 91, 16, 101, 32, 207, 73, 130, 74, 128, 76, 201, 16, 248, 41, 205, 103, 84, 91, 99, 79, 202, 22, 131, 63, 255, 20, 16],
"345y"
))
flag{30831242-56db-45b4-96fd-1f47e60da99d}
babycode
拿到题目,发现后缀是mrb
的,然后文件头是RITEXXXX
,所以肯定是mruby
的字节码了。
那就先用./mruby -v
去提取字节码的汇编吧。
得到:
irep 0x55cb1570a510 nregs=5 nlocals=2 pools=1 syms=5 reps=2 ilen=55
local variable names:
R1:p
000 LOADNIL R2
002 LOADNIL R3
004 CLASS R2 :Crypt
007 EXEC R2 I(0:0x55cb1570a5c0)
010 TCLASS R2
012 METHOD R3 I(1:0x55cb1570adc0)
015 DEF R2 :check
018 SSEND R2 :gets n=0 (0x00)
022 SEND R2 :chomp n=0 (0x00)
026 MOVE R1 R2 ; R1:p
029 MOVE R3 R1 ; R1:p
032 SSEND R2 :check n=1 (0x01)
036 JMPNOT R2 050
040 STRING R3 L(0) ; yes
043 SSEND R2 :puts n=1 (0x01)
047 JMP 052
050 LOADNIL R2
052 RETURN R2
054 STOP
irep 0x55cb1570a5c0 nregs=3 nlocals=1 pools=0 syms=1 reps=1 ilen=12
000 LOADNIL R1
002 LOADNIL R2
004 CLASS R1 :CIPHER
007 EXEC R1 I(0:0x55cb1570a690)
010 RETURN R1
irep 0x55cb1570a690 nregs=3 nlocals=1 pools=0 syms=6 reps=4 ilen=55
000 LOADI32 R1 305419896
006 SETCONST XX R1
009 LOADI R1 16
012 SETCONST YY R1
015 LOADSELF R1
017 SCLASS R1
019 METHOD R2 I(0:0x55cb1570a7d0)
022 DEF R1 :encrypt
025 TCLASS R1
027 METHOD R2 I(1:0x55cb1570a870)
030 DEF R1 :encrypt
033 SSEND R1 :private n=0 (0x00)
037 TCLASS R1
039 METHOD R2 I(2:0x55cb1570ab90)
042 DEF R1 :to_key
045 TCLASS R1
047 METHOD R2 I(3:0x55cb1570ac60)
050 DEF R1 :enc_one
053 RETURN R1
irep 0x55cb1570a7d0 nregs=9 nlocals=5 pools=0 syms=3 reps=0 ilen=29
local variable names:
R1:t
R2:p
R3:&
R4:cip
000 ENTER 2:0:0:0:0:0:0 (0x80000)
004 GETCONST R5 CIPHER
007 SEND R5 :new n=0 (0x00)
011 MOVE R4 R5 ; R4:cip
014 MOVE R5 R4 ; R4:cip
017 MOVE R6 R1 ; R1:t
020 MOVE R7 R2 ; R2:p
023 SEND R5 :encrypt n=2 (0x02)
027 RETURN R5
irep 0x55cb1570a870 nregs=16 nlocals=11 pools=1 syms=8 reps=1 ilen=346
local variable names:
R1:t
R2:p
R3:&
R4:key
R5:c
R6:n
R7:num1
R8:num2
R9:enum1
R10:enum2
000 ENTER 2:0:0:0:0:0:0 (0x80000)
004 MOVE R12 R2 ; R2:p
007 SSEND R11 :to_key n=1 (0x01)
011 MOVE R4 R11 ; R4:key
014 ARRAY R5 R5 0 ; R5:c
017 LOADI_0 R6 ; R6:n
019 MOVE R11 R6 ; R6:n
022 MOVE R12 R1 ; R1:t
025 SEND R12 :length n=0 (0x00)
029 LT R11 R12
031 JMPNOT R11 327
035 MOVE R11 R1 ; R1:t
038 MOVE R12 R6 ; R6:n
041 GETIDX R11 R12
043 SEND R11 :ord n=0 (0x00)
047 SEND R11 :to_i n=0 (0x00)
051 LOADI R12 24
054 SEND R11 :<< n=1 (0x01)
058 MOVE R7 R11 ; R7:num1
061 MOVE R11 R7 ; R7:num1
064 MOVE R12 R1 ; R1:t
067 MOVE R13 R6 ; R6:n
070 ADDI R13 1
073 GETIDX R12 R13
075 SEND R12 :ord n=0 (0x00)
079 SEND R12 :to_i n=0 (0x00)
083 LOADI R13 16
086 SEND R12 :<< n=1 (0x01)
090 ADD R11 R12
092 MOVE R7 R11 ; R7:num1
095 MOVE R11 R7 ; R7:num1
098 MOVE R12 R1 ; R1:t
101 MOVE R13 R6 ; R6:n
104 ADDI R13 2
107 GETIDX R12 R13
109 SEND R12 :ord n=0 (0x00)
113 SEND R12 :to_i n=0 (0x00)
117 LOADI R13 8
120 SEND R12 :<< n=1 (0x01)
124 ADD R11 R12
126 MOVE R7 R11 ; R7:num1
129 MOVE R11 R7 ; R7:num1
132 MOVE R12 R1 ; R1:t
135 MOVE R13 R6 ; R6:n
138 ADDI R13 3
141 GETIDX R12 R13
143 SEND R12 :ord n=0 (0x00)
147 SEND R12 :to_i n=0 (0x00)
151 ADD R11 R12
153 MOVE R7 R11 ; R7:num1
156 MOVE R11 R1 ; R1:t
159 MOVE R12 R6 ; R6:n
162 ADDI R12 4
165 GETIDX R11 R12
167 SEND R11 :ord n=0 (0x00)
171 SEND R11 :to_i n=0 (0x00)
175 LOADI R12 24
178 SEND R11 :<< n=1 (0x01)
182 MOVE R8 R11 ; R8:num2
185 MOVE R11 R8 ; R8:num2
188 MOVE R12 R1 ; R1:t
191 MOVE R13 R6 ; R6:n
194 ADDI R13 5
197 GETIDX R12 R13
199 SEND R12 :ord n=0 (0x00)
203 SEND R12 :to_i n=0 (0x00)
207 LOADI R13 16
210 SEND R12 :<< n=1 (0x01)
214 ADD R11 R12
216 MOVE R8 R11 ; R8:num2
219 MOVE R11 R8 ; R8:num2
222 MOVE R12 R1 ; R1:t
225 MOVE R13 R6 ; R6:n
228 ADDI R13 6
231 GETIDX R12 R13
233 SEND R12 :ord n=0 (0x00)
237 SEND R12 :to_i n=0 (0x00)
241 LOADI R13 8
244 SEND R12 :<< n=1 (0x01)
248 ADD R11 R12
250 MOVE R8 R11 ; R8:num2
253 MOVE R11 R8 ; R8:num2
256 MOVE R12 R1 ; R1:t
259 MOVE R13 R6 ; R6:n
262 ADDI R13 7
265 GETIDX R12 R13
267 SEND R12 :ord n=0 (0x00)
271 SEND R12 :to_i n=0 (0x00)
275 ADD R11 R12
277 MOVE R8 R11 ; R8:num2
280 MOVE R12 R7 ; R7:num1
283 MOVE R13 R8 ; R8:num2
286 MOVE R14 R4 ; R4:key
289 SSEND R11 :enc_one n=3 (0x03)
293 AREF R9 R11 0 ; R9:enum1
297 AREF R10 R11 1 ; R10:enum2
301 MOVE R11 R5 ; R5:c
304 MOVE R12 R9 ; R9:enum1
307 SEND R11 :<< n=1 (0x01)
311 MOVE R11 R5 ; R5:c
314 MOVE R12 R10 ; R10:enum2
317 SEND R11 :<< n=1 (0x01)
321 ADDI R6 8 ; R6:n
324 JMP 019
327 MOVE R11 R5 ; R5:c
330 BLOCK R12 I(0:0x55cb1570aac0)
333 SENDB R11 :collect n=0 (0x00)
337 STRING R12 L(0) ;
340 SEND R11 :join n=1 (0x01)
344 RETURN R11
irep 0x55cb1570aac0 nregs=7 nlocals=3 pools=1 syms=1 reps=0 ilen=16
local variable names:
R1:x
R2:&
000 ENTER 1:0:0:0:0:0:0 (0x40000)
004 STRING R4 L(0) ; %.8x
007 MOVE R5 R1 ; R1:x
010 SSEND R3 :sprintf n=2 (0x02)
014 RETURN R3
irep 0x55cb1570ab90 nregs=6 nlocals=3 pools=1 syms=1 reps=0 ilen=16
local variable names:
R1:p
R2:&
000 ENTER 1:0:0:0:0:0:0 (0x40000)
004 MOVE R3 R1 ; R1:p
007 STRING R4 L(0) ; L*
010 SEND R3 :unpack n=1 (0x01)
014 RETURN R3
irep 0x55cb1570ac60 nregs=11 nlocals=8 pools=0 syms=2 reps=1 ilen=42
local variable names:
R1:num1
R2:num2
R3:key
R4:&
R5:y
R6:z
R7:s
000 ENTER 3:0:0:0:0:0:0 (0xc0000)
004 MOVE R8 R1 ; R1:num1
007 MOVE R9 R2 ; R2:num2
010 LOADI_0 R10
012 MOVE R5 R8 ; R5:y
015 MOVE R6 R9 ; R6:z
018 MOVE R7 R10 ; R7:s
021 GETCONST R8 YY
024 BLOCK R9 I(0:0x55cb1570ad30)
027 SENDB R8 :times n=0 (0x00)
031 MOVE R8 R5 ; R5:y
034 MOVE R9 R6 ; R6:z
037 ARRAY R8 R8 2
040 RETURN R8
irep 0x55cb1570ad30 nregs=10 nlocals=3 pools=1 syms=5 reps=0 ilen=186
local variable names:
R1:i
R2:&
000 ENTER 1:0:0:0:0:0:0 (0x40000)
004 GETUPVAR R3 5 0
008 GETUPVAR R4 6 0
012 LOADI_3 R5
014 SEND R4 :<< n=1 (0x01)
018 GETUPVAR R5 6 0
022 LOADI_5 R6
024 SEND R5 :>> n=1 (0x01)
028 SEND R4 :^ n=1 (0x01)
032 GETUPVAR R5 6 0
036 ADD R4 R5
038 GETUPVAR R5 7 0
042 GETUPVAR R6 3 0
046 GETUPVAR R7 7 0
050 LOADI R8 11
053 SEND R7 :>> n=1 (0x01)
057 ADDI R7 1
060 LOADI_3 R8
062 SEND R7 :& n=1 (0x01)
066 GETIDX R6 R7
068 ADD R5 R6
070 SEND R4 :^ n=1 (0x01)
074 ADD R3 R4
076 SETUPVAR R3 5 0
080 LOADL R4 L(0) ; 4294967295
083 SEND R3 :& n=1 (0x01)
087 SETUPVAR R3 5 0
091 GETUPVAR R3 7 0
095 GETCONST R4 XX
098 ADD R3 R4
100 SETUPVAR R3 7 0
104 GETUPVAR R3 6 0
108 GETUPVAR R4 5 0
112 LOADI_3 R5
114 SEND R4 :<< n=1 (0x01)
118 GETUPVAR R5 5 0
122 LOADI_5 R6
124 SEND R5 :>> n=1 (0x01)
128 SEND R4 :^ n=1 (0x01)
132 GETUPVAR R5 5 0
136 ADD R4 R5
138 GETUPVAR R5 7 0
142 GETUPVAR R6 3 0
146 GETUPVAR R7 7 0
150 ADDI R7 1
153 LOADI_3 R8
155 SEND R7 :& n=1 (0x01)
159 GETIDX R6 R7
161 ADD R5 R6
163 SEND R4 :^ n=1 (0x01)
167 ADD R3 R4
169 SETUPVAR R3 6 0
173 LOADL R4 L(0) ; 4294967295
176 SEND R3 :& n=1 (0x01)
180 SETUPVAR R3 6 0
184 RETURN R3
irep 0x55cb1570adc0 nregs=13 nlocals=8 pools=2 syms=7 reps=0 ilen=128
local variable names:
R1:p
R2:&
R3:i
R4:lst_ch
R5:c
R6:k
R7:cipher_text
000 ENTER 1:0:0:0:0:0:0 (0x40000)
004 LOADI_0 R3 ; R3:i
006 LOADI_0 R4 ; R4:lst_ch
008 MOVE R8 R3 ; R3:i
011 MOVE R9 R1 ; R1:p
014 SEND R9 :length n=0 (0x00)
018 LT R8 R9
020 JMPNOT R8 086
024 MOVE R8 R1 ; R1:p
027 MOVE R9 R3 ; R3:i
030 GETIDX R8 R9
032 SEND R8 :ord n=0 (0x00)
036 MOVE R5 R8 ; R5:c
039 MOVE R8 R5 ; R5:c
042 MOVE R9 R4 ; R4:lst_ch
045 SEND R8 :^ n=1 (0x01)
049 MOVE R9 R3 ; R3:i
052 ADDI R9 1
055 SEND R8 :^ n=1 (0x01)
059 SEND R8 :chr n=0 (0x00)
063 MOVE R9 R1 ; R1:p
066 MOVE R10 R3 ; R3:i
069 MOVE R11 R8
072 SETIDX R9 R10 R11
074 MOVE R8 R5 ; R5:c
077 MOVE R4 R8 ; R4:lst_ch
080 ADDI R3 1 ; R3:i
083 JMP 008
086 STRING R6 L(0) ; aaaassssddddffff ; R6:k
089 GETCONST R8 Crypt
092 GETMCNST R8 R8::CIPHER
095 MOVE R9 R1 ; R1:p
098 MOVE R10 R6 ; R6:k
101 SEND R8 :encrypt n=2 (0x02)
105 MOVE R7 R8 ; R7:cipher_text
108 MOVE R8 R7 ; R7:cipher_text
111 STRING R9 L(1) ; f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb
114 EQ R8 R9
116 JMPNOT R8 124
120 LOADT R8
122 RETURN R8
124 LOADF R8
126 RETURN R8
先看开头的0x55cb1570a510
代码块,大概意思就是定义Crypt
类,然后定义的时候执行0x55cb1570a5c0
初始化,执行再给这个类加上个0x55cb1570adc0
叫做check
函数,然后就是gets
输入,赋值给p
,再调用check(p)
,最后判断返回的逻辑值。
那就接着看0x55cb1570a5c0
初始化了啥东西,实际上就是执行了下0x55cb1570a690
。
那就接着看0x55cb1570a690
,先是定义了俩常量XX=305419896
,YY=16
,然后加了0x55cb1570a7d0
和0x55cb1570a870
都叫encrypt
(至于为啥能有俩同名函数,应该是重载之类的?不太清楚,ruby
总不能连重载特性都没有吧),然后0x55cb1570ab90
叫to_key
,0x55cb1570ac60
叫enc_one
,就完事了。
那着时候就去看主要的逻辑check
,也就是0x55cb1570adc0
,可以看出传参是p
,局部变量是i
、lst_ch
、c
、k
、cipher_text
,然后根据几个LT
指令和JMP
、JMPNOT
指令,可以看出是对i
做循环,条件是for i in range(len(p))
。
然后024~030
就是取p[i]
,然后032~036
把取的结果ord
一下再赋值给c
,039~045
取c
和lst_ch
异或,其中lst_ch
在上面定义过,LOADI_0
就说明初始值是0
,然后049~052
是取个i+1
,055
是把上面的c^lst_ch
和这个i+1
异或,然后059
把异或结果chr
回来,063~072
表示给p[i]
赋值上面的chr
结果,074~080
说明把c
赋值给lst_ch
。
然后080
说明循环步长是1
,086
循环结束,定义k="aaaassssddddffff"
,然后089~105
调用Crypt.CIPHER.encrypt(p, k)
,赋值给cipher_text
,最后108~114
比较cipher_text
和常量f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb
,相等即通过。
所以再去看encrypt
,先看0x55cb1570a7d0
的,传参是t, p
,好像就是包装了下另一个在0x55cb1570a870
的encrypt
。
那就看0x55cb1570a870
,传参还是t, p
,其中004~011
就是key = to_key(p)
,014
和017
分别定义c=[]
和n=0
。
接着又是一个循环,可以看出是while n < len(t): n += 8
然后循环内035~153
其实就是:
num1 = ord(t[n]) << 24
num1 += ord(t[n+1]) << 16
num1 += ord(t[n+2]) << 8
num1 += ord(t[n+3])
同理下面156~277
就是:
num2 = ord(t[n+4]) << 24
num2 += ord(t[n+5]) << 16
num2 += ord(t[n+6]) << 8
num2 += ord(t[n+7])
然后280~317
是:
enum1, enum2 = enc_one(num1, num2, key)
c.append(enum1)
c.append(enum2)
出了循环后,是调用了"".join(c.collect(0x55cb1570aac0))
。
而0x55cb1570aac0
就是进制转换。
to_key
的0x55cb1570ab90
就是每4字节转成一个整数。
enc_one
的0x55cb1570ac60
就是YY.times
循环0x55cb1570ad30
,也就是循环YY
次,然后返回y, z
。
最后0x55cb1570ad30
,就是一堆位操作,记好各个寄存器就行:
y = y + ((((z << 3) ^ (z >> 5)) + z) ^ (s + key[((s >> 11) + 1) & 3]))
y = y & 0xffffffff
s = s + XX
z = z + ((((y << 3) ^ (y >> 5)) + y) ^ (s + key[(s + 1) & 3]))
z = z & 0xffffffff
最后转换的python
代码如下:
def main(p):
return check(p)
def encrypt(t, p):
key = to_key(p)
c = []
n = 0
while n < len(t):
# num1 = int(t[n:n + 4].hex(), 16)
num1 = ord(t[n]) << 24
num1 += ord(t[n+1]) << 16
num1 += ord(t[n+2]) << 8
num1 += ord(t[n+3])
# num2 = int(t[n+4:n+8].hex(), 16)
num2 = ord(t[n+4]) << 24
num2 += ord(t[n+5]) << 16
num2 += ord(t[n+6]) << 8
num2 += ord(t[n+7])
enum1, enum2 = enc_one(num1, num2, key)
c.append(enum1)
c.append(enum2)
n += 8
return "".join([("00000000"+hex(i)[2:])[-8:] for i in c])
def to_key(p):
a = []
for i in range(0, len(p), 4):
a.append(int(p.encode()[i:i+4].hex(), 16))
return a
def enc_one(num1, num2, key):
y = num1 # R5
z = num2 # R6
s = 0 # R7
for _ in range(YY):
y = y + ((((z << 3) ^ (z >> 5)) + z) ^ (s + key[((s >> 11) + 1) & 3]))
y = y & 0xffffffff
s = s + XX
z = z + ((((y << 3) ^ (y >> 5)) + y) ^ (s + key[(s + 1) & 3]))
z = z & 0xffffffff
return y, z
def check(p):
p = bytearray(p.encode())
lst_ch = 0
for i in range(len(p)):
c = p[i]
p[i] = (c ^ lst_ch) ^ (i + 1)
lst_ch = c
k = "aaaassssddddffff"
print(p.hex())
cipher_text = encrypt(p, k)
return cipher_text
if __name__ == '__main__':
XX = 305419896
YY = 16
main(flag)
所以写出逆向逻辑就行。
def to_key(p):
a = []
for i in range(0, len(p), 4):
a.append(int(p.encode()[i:i+4].hex(), 16))
return a
c = "f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb"
XX = 305419896
YY = 16
key = to_key("aaaassssddddffff")
dec = bytearray(40)
for i in range(0, len(c), 16):
num1 = int(c[i:i+8], 16)
num2 = int(c[i+8:i+16], 16)
y = num1 # R5
z = num2 # R6
s = 0
for _ in range(YY):
s = s + XX
for _ in range(YY):
z = z - ((((y << 3) ^ (y >> 5)) + y) ^ (s + key[(s + 1) & 3]))
z = z & 0xffffffff
s = s - XX
y = y - ((((z << 3) ^ (z >> 5)) + z) ^ (s + key[((s >> 11) + 1) & 3]))
y = y & 0xffffffff
dec[i//2:i//2+4] = bytes.fromhex(("0000"+hex(y)[2:])[-8:])
dec[i//2+4:i//2+8] = bytes.fromhex(("0000"+hex(z)[2:])[-8:])
print(dec.hex())
lst_ch = 0
for i in range(len(dec)):
c = dec[i]
dec[i] = (c ^ (i + 1)) ^ lst_ch
lst_ch = dec[i]
print(dec)
flag{6ad1c70c-daa4-11ec-9d64-0242ac1200}
Comments 5 条评论
博主 STARBOY
作者您好!请问mruby的每个opcode符号含义如何查询?比如SSEND这种就不知道其意义。
另外ast语法树如何了解?现场想手撕真的无从下手!感谢答复🙏
博主 Wankko Ree
@STARBOY 这次其实我也是现场手撕的,之前没接触过Swift的ast和mruby汇编,资料的话我也没找到多少,主要就参考这篇:https://www.anquanke.com/post/id/253572 快速入门一下,Swift的ast资料是一点都没找到。
我的思路ast就是当阅读理解做,先总体看一下格式,比如缩进、首尾标记这些,然后就是找同级的相似代码块进行对比,看看哪些地方有变化,然后之后就重点关注这些点,也就是我文里提到那些高亮关键词。然后就可以连蒙带猜转化成自己熟悉的语言了。
mruby指令的话,其实我是纯靠猜的,结合指令上方的寄存器和指令本身的寄存器,其实可以猜出大概用途的。
博主 STARBOY
@Wankko Ree 感谢回复🙏,我太菜了,学习
博主 Wankko Ree
@STARBOY 比如说ssend后面跟了个n=几,然后上面正好有几个寄存器和ssend指向的那个函数参数个数一样,所以就猜测ssend意思是把最近的n个寄存器当作参数调用目标函数。
博主 STARBOY
@Wankko Ree 原来如此,思路很重要,学习了🙏