Mobile wallpaper 1
2947 字
15 分钟
2025华为杯实网对抗赛初赛 Reverse 部分题目 WP

前言#

说实话,这比赛办的十分有九分的抽象的,就这次突然要求集中参赛打了我们一个措手不及,直接等于是被动放弃资格了(

好了,不多吐槽,直接进入正题。这次的Reverse还挺好玩的,难度也不是很大,让我一个学pwn的都搓出来了两道

GoEnc#

这是一道经典的Go语言逆向。

打开IDA后就能够看到Go语言的库函数满天飞,但是实际上检查以及加解密都在main文件夹里

首先看init就能够找到加密后的数据

image

Data: JBaoWyDrnxq47qscXupR5W+zxqUHT/Z0h9Qjh97aSuM09nZH7AauwGLHhDE1KKdj

这一看就是base64

我们先跟着逻辑过一遍

前面的 flag{头检查和长度检查我就一笔带过了

image

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

image

好的,这里就确定了数据在最后进行了一次base64加密

继续跟进 v36 对应的函数

image

能看到是一个十分经典的 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
}

imageimage

对于 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反编译放出来

image

image

从这些就能够扒出来这样一个算法

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 base64
from Crypto.Cipher import AES
from 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 base64
from Crypto.Cipher import AES
from 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编译器在编译中的中间代码,也就是说我们对于这一题,有两种解法

  1. 使用 clang 将其编译为可执行文件 clang file.bc -o name
  2. 使用llvm-dis将其转化为汇编,看汇编得flag

显然,我们第一种解法对于人眼看是最简单的。

编译后解题#

image

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

image

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

image

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

image

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

image

image

在运算为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

image

生成密钥

16字节的密钥由 5个 Part1字符及其sha256结果的前11字节组成

image

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

image

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

image

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

image

Part3经过一个S_BOX加密后进行校验

image

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 hashlib
import 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
# 求解input1
input1 = solve_input1()
if not input1:
print("Part1 Not Found")
exit(-1)
# 求解input2
input2 = solve_input2(input1)
# 求解input3
input3 = solve_input3()
# 输出最终结果
print(f"input1: {input1}")
print(f"input2: {input2}")
print(f"input3: {input3}")
print(f"flag: {input1}_{input2}_{input3}")

运行得结果

image

扔到elf里面做校验

image

完成

llvm-dis 解题#

因为这个比赛初赛是线上的,也就是说可以直接将反汇编的代码扔给ai。

据某不知名大佬所说,chatgpt只用了四分钟就把这道题的flag发给了他,chatgpt tql

这里复现一下

image

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

image

image

image

可以看到,这个程序实际只有前面这么多,甚至changpt找出了我写的程序的检查的错误。就是用时没有大佬讲的那么夸张,大概用时是9分钟(没有算排队的时间)

ooops(未解出)#

这个题目我是没招了。

题目给了一个elf文件,可以通过ida打开知道,这个程序是由pyinstaller打包获得的。但是如果你想用 pyinstxtractor解包,你会发现它识别不了这个题目的pyinstaller。线上工具也是。

image

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

2025华为杯实网对抗赛初赛 Reverse 部分题目 WP
https://blog.mindedness.top/posts/2025华为杯实网对抗赛初赛-reverse-部分题目-wp/
作者
Mindedness
发布于
2025-11-23
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00