前言
说实话,这比赛办的十分有九分的抽象的,就这次突然要求集中参赛打了我们一个措手不及,直接等于是被动放弃资格了(
好了,不多吐槽,直接进入正题。这次的Reverse还挺好玩的,难度也不是很大,让我一个学pwn的都搓出来了两道
GoEnc
这是一道经典的Go语言逆向。
打开IDA后就能够看到Go语言的库函数满天飞,但是实际上检查以及加解密都在main文件夹里
首先看init就能够找到加密后的数据

Data: JBaoWyDrnxq47qscXupR5W+zxqUHT/Z0h9Qjh97aSuM09nZH7AauwGLHhDE1KKdj
这一看就是base64
我们先跟着逻辑过一遍
前面的 flag{头检查和长度检查我就一笔带过了

能看到这个函数只要通过,就会输出congratulations,那么大概率加密逻辑就在里面了

好的,这里就确定了数据在最后进行了一次base64加密
继续跟进 v36 对应的函数

能看到是一个十分经典的 AES-CBC 加密,也就是说前面给了一个 aes_key 和一个 iv,这两个都是在前面的逻辑得到的
我们对照go语言的写法和传参来看这个加密
/* CBC 加密 text 待加密的明文 key 秘钥*/func CBCEncrypter(text []byte,key []byte,iv []byte) []byte{ block,err:=aes.NewCipher(key) if err !=nil { fmt.Println(err) } // 填充 paddText := PKCS7Padding(text,block.BlockSize())
blockMode := cipher.NewCBCEncrypter(block,iv)
// 加密 result := make([]byte,len(paddText)) blockMode.CryptBlocks(result,paddText) // 返回密文 return result}

对于 NewCBCEncrypter而言,参数分别为 self, block, iv
也就是说,这个a4,也就是 off_55D8A0对应aes_key
a7, off_55D8C0对应的是iv
这样,我们就有两种玩法,第一种是直接去相应位置把值扣下来,第二种是把算法扒光衣服,完全解密
比赛的时候我其实选的第一种(
这里简单讲一下第一种的做法
直接patch掉对attach的检测(我直接给jz patch成 jnz)
之后直接运到相应位置
直接看当前两者对应的东西
# aes key[+] Dump 0x54A6D0 - 0x54A6DF (16 bytes) :"\x2A\x3E\x5B\x68\xFE\x38\x27\x05\x4C\xA4\xD9\x35\xFE\xAD\xB2\x6F"# iv[+] Dump 0x54A6E0 - 0x54A6EF (16 bytes) :"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x61\x62\x63\x64\x65\x66"直接就用这个跑aes_cbc就好啦
但是现在比赛打完了,我肯定是要把它扒光的 XD
(虽然iv还是要动调一下)
这里主要扒的是aes的密钥生成逻辑.
这里将这个aes密钥生成的ida反编译放出来


从这些就能够扒出来这样一个算法
def get_aes_key(): key1 = bytearray(b"MySecretKey12345") key2 = b"\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44\x55\x66\x77\x88\x99\x00"
def rol(value, shift): return ((value << shift) | (value >> (8 - shift))) & 0xFF
def loop_func(aes_key, key2, round): tmp1 = (23 * round + 77) & 0xFF for i in range(16): tmp2 = rol( (tmp1^aes_key[i]), 2 ) aes_key[i] = ( i+1+ (key2[i] ^ tmp2) ) & 0xFF return aes_key
for i in range(6): key2 = loop_func(key1, key2, i) final = bytes(key1) return final我们优化一下,就得到了这样的一个算法
def get_aes_key(): key1 = bytearray(b"MySecretKey12345") key2 = b"\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44\x55\x66\x77\x88\x99\x00" for i in range(6): for j in range(16): v2 = (23 * i + 77) & 0xFF v5 = ((v2 ^ key1[j]) << 2 | (v2 ^ key1[j]) >> (8 - 2)) & 0xFF v4 = key2[j] ^ v5 key1[j] = (i + 1 + v4) & 0xFF final_key = bytes(key1) return final_key也就是说,我们的总体脚本是这样的
#!C:\\Python313\\python.exe python3# -*- coding: utf-8 -*-import base64from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpad
encrypted_b64 = "JBaoWyDrnxq47qscXupR5W+zxqUHT/Z0h9Qjh97aSuM09nZH7AauwGLHhDE1KKdj"
def get_aes_key(): # 模拟程序中的密钥生成算法,得到最终的AES密钥 key1 = bytearray(b"MySecretKey12345") key2 = b"\xaa\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44\x55\x66\x77\x88\x99\x00" for i in range(6): for j in range(16): v2 = (23 * i + 77) & 0xFF v5 = ((v2 ^ key1[j]) << 2 | (v2 ^ key1[j]) >> (8 - 2)) & 0xFF v4 = key2[j] ^ v5 key1[j] = (i + 1 + v4) & 0xFF final_key = bytes(key1) return final_key
def decrypt_aes_cbc(encrypt_data, aes_key, iv): # 使用AES CBC模式解密数据 cipher = AES.new(aes_key, AES.MODE_CBC, iv) decrypted = unpad(cipher.decrypt(encrypt_data), AES.block_size) return decrypted.decode()
if __name__ == "__main__": aes_key = get_aes_key() iv = b"1234567890abcdef" # 固定的IV encrypted_data = base64.b64decode(encrypted_b64) flag = decrypt_aes_cbc(encrypted_data, aes_key, iv) print("Decrypted flag:", flag)如果两个都用动调,那么就是直接这么写了
#!C:\\Python313\\python.exe python3# -*- coding: utf-8 -*-import base64from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpad
encrypted_b64 = "JBaoWyDrnxq47qscXupR5W+zxqUHT/Z0h9Qjh97aSuM09nZH7AauwGLHhDE1KKdj"aes_key = b"\x2a\x3e\x5b\x68\xfe\x38\x27\x05\x4c\xa4\xd9\x35\xfe\xad\xb2\x6f"iv = b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x61\x62\x63\x64\x65\x66"
def decrypt_aes_cbc(encrypt_data, aes_key, iv): # 使用AES CBC模式解密数据 cipher = AES.new(aes_key, AES.MODE_CBC, iv) decrypted = unpad(cipher.decrypt(encrypt_data), AES.block_size) return decrypted.decode()
if __name__ == "__main__": encrypted_data = base64.b64decode(encrypted_b64) flag = decrypt_aes_cbc(encrypted_data, aes_key, iv) print("Decrypted flag:", flag)bc
这道题目给了你一个bc.bc的文件
.bc 文件是 LLVM编译器在编译中的中间代码,也就是说我们对于这一题,有两种解法
- 使用 clang 将其编译为可执行文件
clang file.bc -o name - 使用llvm-dis将其转化为汇编,看汇编得flag
显然,我们第一种解法对于人眼看是最简单的。
编译后解题

直接编译会发现有 undefined reference,我们去查阅资料,能够知道这个报错是因为这次编译需要用到 OpenSSL 的函数,因而我们需要加上这个参数 -lcrypto

编译通过,接下来就是看ida

程序将输入按照_分为三部分,分别进行验证

第一段为五个字符,由 0~9和a~f组成


在运算为SHA256后与s2存储的数据相同,即为通过校验
def solve_input1(): target = b'\xEB\x34\x04\x9C\xED\x1F\x58\x30\x16\xDC\x89\x52\xC6\x8A\xAA\x0B\x0F\xB1\xED\x39\x20\xB6\xDC\x40\x89\xEA\xAF\x56\xB8\x40\x2F\xCB'
for i in range(0x10000, 0x100000): candidate = f"{i:05x}" if hashlib.sha256(candidate.encode()).digest() == target: return candidate return None
生成密钥
16字节的密钥由 5个 Part1字符及其sha256结果的前11字节组成

rot运算加上异或加密扩展出最后的key

再经过一个使用key加密的算法得到 encData,与相应加密后的 Part2比较

32轮rot、add、xor加密
def solve_input2(input1): def ROR(value, shift): """64位右旋转""" shift = shift % 64 return ((value >> shift) | (value << (64 - shift))) & 0xffffffffffffffff def ROL(value, shift): """64位左旋转""" shift = shift % 64 return ((value << shift) | (value >> (64 - shift))) & 0xffffffffffffffff def key_extend(key_bytes): # 将16字节密钥转换为两个64位整数 key1 = struct.unpack('<Q', key_bytes[8:16])[0] # key[1] key0 = struct.unpack('<Q', key_bytes[0:8])[0] # key[0]
expanded_keys = []
for i in range(32): # 存储当前轮密钥 expanded_keys.append((key1, key0))
# 更新密钥 key1 = ROR(key1, 8) key1 = (key1 + key0) & 0xffffffffffffffff key1 = key1 ^ i
key0 = ROL(key0, 3) key0 = key0 ^ key1
return expanded_keys def decrypt_block(ciphertext, expanded_keys): left, right = ciphertext
# 逆向32轮加密 for i in range(31, -1, -1): round_key = expanded_keys[i]
# 逆向加密操作 left = left ^ right left = ROR(left, 3)
right = right ^ round_key[1] right = (right - left) & 0xffffffffffffffff right = ROL(right, 8)
return (left, right)
target_cipher_raw = [3925615762734852688, -5047078219416056272] # 转换为无符号64位 target_cipher = [ target_cipher_raw[0] & 0xffffffffffffffff, target_cipher_raw[1] & 0xffffffffffffffff ]
# 生成密钥材料 input1_bytes = input1.encode() sha256_hash = hashlib.sha256(input1_bytes).digest()
# 构造16字节密钥 (input1 + sha256前11字节) key_material = input1_bytes + sha256_hash[:11]
# 密钥扩展 expanded_keys = key_extend(key_material)
# 解密 plaintext = decrypt_block(target_cipher, expanded_keys)
# 转换为字节串 plain_bytes = struct.pack('<QQ', plaintext[0], plaintext[1])
# 去除填充的零字节并解码 result = plain_bytes.rstrip(b'\x00') try: result_str = result.decode('utf-8') except: result_str = result.decode('latin-1')
return result_str
Part3经过一个S_BOX加密后进行校验

def solve_input3(): sbox = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
# 创建逆S-box表 inv_sbox = [0] * 256 for i in range(256): inv_sbox[sbox[i]] = i
# 目标字节序列 target = b'\x1A\x04\x43\x4D\xE3\x09\x9A\x51\xC7\x9F\x85\x00'
# 逆向求解 result = "" for byte in target: result += chr(inv_sbox[byte]) return result最后组成完整解密函数
import hashlibimport struct
def solve_input1(): target = b'\xEB\x34\x04\x9C\xED\x1F\x58\x30\x16\xDC\x89\x52\xC6\x8A\xAA\x0B\x0F\xB1\xED\x39\x20\xB6\xDC\x40\x89\xEA\xAF\x56\xB8\x40\x2F\xCB'
for i in range(0x10000, 0x100000): candidate = f"{i:05x}" if hashlib.sha256(candidate.encode()).digest() == target: return candidate return None
def solve_input2(input1): def ROR(value, shift): """64位右旋转""" shift = shift % 64 return ((value >> shift) | (value << (64 - shift))) & 0xffffffffffffffff def ROL(value, shift): """64位左旋转""" shift = shift % 64 return ((value << shift) | (value >> (64 - shift))) & 0xffffffffffffffff def key_extend(key_bytes): # 将16字节密钥转换为两个64位整数 key1 = struct.unpack('<Q', key_bytes[8:16])[0] # key[1] key0 = struct.unpack('<Q', key_bytes[0:8])[0] # key[0]
expanded_keys = []
for i in range(32): # 存储当前轮密钥 expanded_keys.append((key1, key0))
# 更新密钥 key1 = ROR(key1, 8) key1 = (key1 + key0) & 0xffffffffffffffff key1 = key1 ^ i
key0 = ROL(key0, 3) key0 = key0 ^ key1
return expanded_keys def decrypt_block(ciphertext, expanded_keys): left, right = ciphertext
# 逆向32轮加密 for i in range(31, -1, -1): round_key = expanded_keys[i]
# 逆向加密操作 left = left ^ right left = ROR(left, 3)
right = right ^ round_key[1] right = (right - left) & 0xffffffffffffffff right = ROL(right, 8)
return (left, right)
# 目标密文 - 注意符号转换 target_cipher_raw = [3925615762734852688, -5047078219416056272] # 转换为无符号64位 target_cipher = [ target_cipher_raw[0] & 0xffffffffffffffff, target_cipher_raw[1] & 0xffffffffffffffff ]
# 生成密钥材料 input1_bytes = input1.encode() sha256_hash = hashlib.sha256(input1_bytes).digest()
# 构造16字节密钥 (input1 + sha256前11字节) key_material = input1_bytes + sha256_hash[:11]
# 密钥扩展 expanded_keys = key_extend(key_material)
# 解密 plaintext = decrypt_block(target_cipher, expanded_keys)
# 转换为字节串 plain_bytes = struct.pack('<QQ', plaintext[0], plaintext[1])
# 去除填充的零字节并解码 result = plain_bytes.rstrip(b'\x00') try: result_str = result.decode('utf-8') except: result_str = result.decode('latin-1')
return result_str
def solve_input3(): sbox = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
# 创建逆S-box表 inv_sbox = [0] * 256 for i in range(256): inv_sbox[sbox[i]] = i
# 目标字节序列 target = b'\x1A\x04\x43\x4D\xE3\x09\x9A\x51\xC7\x9F\x85\x00'
# 逆向求解 result = "" for byte in target: result += chr(inv_sbox[byte]) return result
# 求解input1input1 = solve_input1()if not input1: print("Part1 Not Found") exit(-1)# 求解input2input2 = solve_input2(input1)# 求解input3input3 = solve_input3()
# 输出最终结果print(f"input1: {input1}")print(f"input2: {input2}")print(f"input3: {input3}")print(f"flag: {input1}_{input2}_{input3}")运行得结果

扔到elf里面做校验

完成
llvm-dis 解题
因为这个比赛初赛是线上的,也就是说可以直接将反汇编的代码扔给ai。
据某不知名大佬所说,chatgpt只用了四分钟就把这道题的flag发给了他,chatgpt tql
这里复现一下

可惜第一问的part3错了,不然就是真神了



可以看到,这个程序实际只有前面这么多,甚至changpt找出了我写的程序的检查的错误。就是用时没有大佬讲的那么夸张,大概用时是9分钟(没有算排队的时间)
ooops(未解出)
这个题目我是没招了。
题目给了一个elf文件,可以通过ida打开知道,这个程序是由pyinstaller打包获得的。但是如果你想用 pyinstxtractor解包,你会发现它识别不了这个题目的pyinstaller。线上工具也是。

如果你尝试直接binwalk -e它,得到zlib弄出里面的东西,那么恭喜你,你解压了一大坨出来,一个magic都对不上。说实话,我放弃了(
部分信息可能已经过时