比赛难度中等偏难,主办方修了非预期还没修好,不修了摆烂了。
队伍成绩:425pt/10kill/全国48名/华中赛区3
个人成绩:359pt/6kill/1三血/全国42

Misc

ez_usb[三血]

打开流量包,发现好多USB流量。

file

其中2.8可以确定是键盘。

file

2.4查了下好像是U盘。

file

2.10有个罗技标,查了下产品id应该也是键盘。

file

所以目标放在那俩把键盘上,写一下带过滤的提取,分别提取出两个键盘的流量数据。

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

file

改改通用的键盘流量解码脚本,跑一下。

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)

file

发现一堆十六进制和TabCapsLockDelete,反正大小写对十六进制不影响,所以忽略掉CapsLock,然后Tab键感觉也没啥用,反正就在开头有一次键入,至于Delete的话这里意思应该是删掉前一个键入,所以最终得到的两串十六进制是:

526172211a0700cf907300000d00000000000000c4527424943500300000002a00000002b9f9b0530778b5541d33080020000000666c61672e747874b9ba013242f3afc000b092c229d6e994167c05a78708b271ffc042ae3d251e65536f9ada87c77406b67d0e6316684766a86e844dc81aa2c72c71348d10c43d7b00400700

35c535765e50074a

前者把十六进制转raw得到一个rar压缩包。

file

打开发现需要密码。

file

尝试用第二个字符串当密码成功解压,得到flag。

flag{20de17cc-d2c1-4b61-bebd-41159ed7172d}

问卷调查

掐点去做的问卷,没赶上后面出问题的时候,直接出了flag交了。

file

flag{Thanksforplayingourgames}

Crypto

基于挑战码的双向认证+基于挑战码的双向认证2

非预期了,直接扫盘就能找到两题的flag。

find / -name '*flag*'

file

flag{81f14d6d-aef0-418b-85cb-0e8d4fc9515a}
flag{34f5fdaf-c373-47fd-afab-01ed2914c11a}

Reverse

baby_tree

拿到题目,发现是个swiftast,没找到啥好用的一键还原成.swift代码的工具,那就强行转成自己擅长的python代码再逆向吧。

file

先当作js高亮一下,方便看代码关系和层次。

file

然后手动标记一下一些重要的关键词,主要如下:

encoded
keyValue
extension
@
i@
b@
k@
value=
r0
r1
r2
r3

file

然后就来分析ast了。

先是定义了个check函数,传参是俩字符串,然后返回逻辑值。根据缩进判断函数主体逻辑在brace_stmt块里。

file

然后是定义了个b变量,初始值是拆分encoded得到的。

file

然后定义的k变量和b差不多,只不过是拆分keyValue的。

file

然后定义了几个未初始化的变量r0r1r2r3

file

然后下面就是一个超大循环体和超大返回语句。

file

先看for循环,迭代器i的定义比较长,...操作符类似于kotlin..,就是个range,然后下面俩argument分别表示开头和结尾,所以开头很简单,是0,结尾是外面一个-操作符,两个元是b.count4,所以现在这个for循环就是for i in range(0, len(b) - 4+1):+1是因为python里结尾不包括,但是swift是包括的。

file

然后看for循环主体部分,主要就是六个感觉像赋值语句的东西。

file

先看第一个,赋值的目标是一个四元组,内容是r0r1r2r3

file

然后值的话,第一个就是b[i],第二个是b[i+1],第三个是b[i+2],第四个是b[i+3]

file

file

所以python代码就是r0, r1, r2, r3 = b[i], b[i+1], b[i+2], b[i+3]

然后看第二个复制块,赋值目标是b[i+0]

file

值是r2 ^ ((k[0] + (r0 >> 4)) & 0xff),一层一层分析操作符及其元就可以得出,不再详细解释。

file

下面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和一个数组常量。

file

然后看函数外面的顶层代码。先是判断了下cmd传参个数,确保>=2

file

然后把arg[1]赋值给data

file

key定义成常量,值是345y

file

然后最后调用上面的check函数,传参是(data, key),把结果赋值给result

file

所以最后代码就是:

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"
    ))

file

flag{30831242-56db-45b4-96fd-1f47e60da99d}

babycode

拿到题目,发现后缀是mrb的,然后文件头是RITEXXXX,所以肯定是mruby的字节码了。

那就先用./mruby -v去提取字节码的汇编吧。

file

得到:

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),最后判断返回的逻辑值。

file

那就接着看0x55cb1570a5c0初始化了啥东西,实际上就是执行了下0x55cb1570a690

file

那就接着看0x55cb1570a690,先是定义了俩常量XX=305419896YY=16,然后加了0x55cb1570a7d00x55cb1570a870都叫encrypt(至于为啥能有俩同名函数,应该是重载之类的?不太清楚,ruby总不能连重载特性都没有吧),然后0x55cb1570ab90to_key0x55cb1570ac60enc_one,就完事了。

file

那着时候就去看主要的逻辑check,也就是0x55cb1570adc0,可以看出传参是p,局部变量是ilst_chckcipher_text,然后根据几个LT指令和JMPJMPNOT指令,可以看出是对i做循环,条件是for i in range(len(p))

然后024~030就是取p[i],然后032~036把取的结果ord一下再赋值给c039~045clst_ch异或,其中lst_ch在上面定义过,LOADI_0就说明初始值是0,然后049~052是取个i+1055是把上面的c^lst_ch和这个i+1异或,然后059把异或结果chr回来,063~072表示给p[i]赋值上面的chr结果,074~080说明把c赋值给lst_ch

然后080说明循环步长是1086循环结束,定义k="aaaassssddddffff",然后089~105调用Crypt.CIPHER.encrypt(p, k),赋值给cipher_text,最后108~114比较cipher_text和常量f469358b7f165145116e127ad6105917bce5225d6d62a714c390c5ed93b22d8b6b102a8813488fdb,相等即通过。

file

所以再去看encrypt,先看0x55cb1570a7d0的,传参是t, p,好像就是包装了下另一个在0x55cb1570a870encrypt

file

那就看0x55cb1570a870,传参还是t, p,其中004~011就是key = to_key(p)014017分别定义c=[]n=0

file

接着又是一个循环,可以看出是while n < len(t): n += 8

file

file

然后循环内035~153其实就是:

num1 = ord(t[n]) << 24
num1 += ord(t[n+1]) << 16
num1 += ord(t[n+2]) << 8
num1 += ord(t[n+3])

file

同理下面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])

file

然后280~317是:

enum1, enum2 = enc_one(num1, num2, key)
c.append(enum1)
c.append(enum2)

file

出了循环后,是调用了"".join(c.collect(0x55cb1570aac0))

file

0x55cb1570aac0就是进制转换。

file

to_key0x55cb1570ab90就是每4字节转成一个整数。

file

enc_one0x55cb1570ac60就是YY.times循环0x55cb1570ad30,也就是循环YY次,然后返回y, z

file

最后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

file

最后转换的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)

file

flag{6ad1c70c-daa4-11ec-9d64-0242ac1200}


The End