强网杯 QWB2023 Reverse Writeup、题型分析

作者:Tree


发布于 2024-03-28 | 最后更新于 2024-03-28

赛题解析

强网先锋

babyre

主函数分析如下
2023-12-18T213604.png
同时注意到有 TLS 反调试
2023-12-18T213728.png
2023-12-18T213816.png
TLS 反调试里修改了数据,考虑动调获取数据
先绕过两个 BeingDebugged
运行到 jz 跳转指令处,修改 ZF 标志为 0
2023-12-18T215532.png
下一个 jz 跳转指令处,修改 ZF 标志为 0
2023-12-18T220305.png
此时再 F9 运行,提示进到主函数里
随便输入长度为 32 的字符串,此时 IDA 进入了 TEA 函数
2023-12-18T220546.png
TEA 函数 IDA 分析有点问题
看看汇编
可以看到实际的 delta 值为 0x88408067
2023-12-18T221055.png
我们可以通过计算获取到解密脚本需要的 sum 值
也可以顺手在该函数中获取一下,运行第二个断点 return 处
查看一下 v4 的值 0xD192C263
2023-12-18T221555.png
最后回到主函数获取一下经过修改的 key 和 enc
2023-12-18T222002.png

 1#include <stdio.h>
 2#include <stdlib.h>
 3#include <stdint.h>
 4void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
 5    unsigned int i,j;
 6    uint32_t v0 = v[0], v1 = v[1], delta = 0x88408067, sum = 0xD192C263;
 7    for (j=0; j< 4; j++){
 8    for (i=0; i < num_rounds; i++) {
 9        sum -= delta;
10        v1 -= (((v0 << 5) ^ (v0 >> 4)) + v0) ^ (sum + key[(sum>>11) & 3]);
11        v0 -= (((v1 << 5) ^ (v1 >> 4)) + v1) ^ (sum + key[sum & 3]) ^ sum;
12        
13    }
14    }
15    v[0] = v0; v[1] = v1;
16}
17int main() {
18    uint32_t const k[4] = {0x62, 0x6F, 0x6D, 0x62};
19    uint32_t enc[] = {
20      0x9523F2E0, 0x8ED8C293, 0x8668C393, 0xDDF250BC, 0x510E4499, 0x8C60BD44, 0x34DCABF2, 0xC10FD260};
21    unsigned int r = 33;
22    for(int i=0; i<8; i+=2){
23        decrypt(r, &enc[i], k);
24    }
25    printf("Decrypted data is: %s\n",(char*)enc);
26    return 0;
27}
28//Decrypted data is: flag{K1k1_S_1ov3_fr0m_Ch1ck_wh0_1s_w0rk1ng_h@rD}4

flag{K1k1_S_1ov3_fr0m_Ch1ck_wh0_1s_w0rk1ng_h@rD}

ezre

OLLVM 混淆控制流平坦化 D810
主函数如下
2023-12-19T001617.png
先看最后一步 sub_401EB0
2023-12-19T000844.png
先逆一下,动调取出 base64_Table
2023-12-19T002300.png
2023-12-19T001904.png
2023-12-19T002034.png
2023-12-19T002122.png
2023-12-19T002158.png

 1enc =[0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04, 0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70, 0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D, 0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70, 0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
 2table = ["l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr",
 3       "FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8",
 4       "Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA",
 5       "pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a",
 6       "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"]
 7v5 = [ord(i) ^ 0x27 for i in table[4]][6:6+0x15]
 8idx = 2023
 9xor = []
10for i in range(47):
11    if i % 3 == 1:
12       idx = (idx + 5) % 20
13       tmp = v5[idx + 1]
14    elif i % 3 == 2:
15       idx = (idx + 7) % 19
16       tmp = v5[idx + 2]
17    else:
18       idx = (idx + 3) % 17
19       tmp = v5[idx + 3]
20    xor.append(tmp)
21for i in range(46, -1, -1):
22    enc[i + 1] ^= enc[i]
23    enc[i] ^= xor[i]
24for i in enc:
25   print(chr(i), end='')
26#WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==

最后再返回去依次用
plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6 解码
pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a 编码
Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA 解码
FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8 编码
l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr 解码
2023-12-19T004209.png

 1import base64
 2enc =[0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04, 0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70, 0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D, 0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70, 0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
 3table = ["l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr",
 4       "FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8",
 5       "Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA",
 6       "pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a",
 7       "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"]
 8v5 = [ord(i) ^ 0x27 for i in table[4]][6:6+0x15]
 9idx = 2023
10xor = []
11for i in range(47):
12    if i % 3 == 1:
13       idx = (idx + 5) % 20
14       tmp = v5[idx + 1]
15    elif i % 3 == 2:
16       idx = (idx + 7) % 19
17       tmp = v5[idx + 2]
18    else:
19       idx = (idx + 3) % 17
20       tmp = v5[idx + 3]
21    xor.append(tmp)
22for i in range(46, -1, -1):
23    enc[i + 1] ^= enc[i]
24    enc[i] ^= xor[i]
25cipher = ''
26for i in enc:
27   cipher += chr(i)
28print(cipher)
29#WZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==
30def custom_base64_decode(data, custom_chars):
31    # 创建标准Base64字符集和自定义字符集的映射表
32    base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
33    char_map = str.maketrans( custom_chars, base64_chars)
34    # 替换为自定义字符
35    data = data.translate(char_map)
36    # 使用标准Base64解码
37    decoded_data = base64.b64decode(data.encode())
38    return decoded_data
39def custom_base64_encode(data, custom_chars):
40    # 创建标准Base64字符集和自定义字符集的映射表
41    base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
42    char_map = str.maketrans(base64_chars, custom_chars)
43    # 使用标准Base64编码
44    encoded_data = base64.b64encode(data).decode()
45    # 替换为自定义字符
46    encoded_data = encoded_data.translate(char_map)
47    return encoded_data
48flag = custom_base64_decode(cipher, table[4])
49flag = custom_base64_encode(flag, table[3])
50flag = custom_base64_decode(flag, table[2])
51flag = custom_base64_encode(flag, table[1])
52flag = custom_base64_decode(flag, table[0])
53print(flag.decode())
54#flag{3ea590ccwxehg715264fzxnzepqz}

flag{3ea590ccwxehg715264fzxnzepqz}

Reverse

ezre

同样的 OLLVM 混淆控制流平坦化 D810
主函数如下
2023-12-21T202310.png
没什么有价值信息,只在 sub_3180 里看到了假 flag
函数不多一个个找,发现 sub_3580 里有个可疑数据,长得很像加密过程
2023-12-21T202638.png
而且调用了 sub_2220 可以看出来是 SM4 加密 signsrch 也识别出来了
2023-12-21T203148.png
直接 cyberchef 梭
2023-12-21T203431.png
flag{h3kk0_w0rld_sur3_3n0ugh}

dotdot

DIE 查看 PE 结构发现是 .NET 程序
用 dnSpy 打开,发现有数据没有反编译出来
改用 ILSpy 打开,正常反编译可以看到数据
AAA 是白盒 AES
BBB 是长度判断,要求长度为 16
CCC 是比较函数,
DDD 是 License.dat 的读取函数
EEE 是将输入作为 key 进行 RC4 解密 License.dat
FFF 里进行 TEA 加密,然后校验加密结果,并且对 License.dat 进行 MD5 校验,校验正确了再异或求出正确 flag
一步步来,先解白盒 AES 可以看到 v14 是 Tboxes
2023-12-22T171348.png
whitebox-aes
解出来主密钥为 QWB2023HappyGame
2023-12-22T171604.png
AES 解密一下 v4 解出 RC4 的 key 也就是输入

1from Crypto.Cipher import AES
2enc = [97, 147, 49, 123, 248, 150, 224, 0, 165, 39, 183, 55, 74, 227, 3, 168]
3key = b'QWB2023HappyGame'
4aes = AES.new(key, AES.MODE_ECB)
5input = aes.decrypt(bytes(enc)).decode()
6print(input)
7# WelcomeToQWB2023

然后 RC4 解密 License.dat

1from Crypto.Cipher import ARC4
2with open('License.dat', 'rb') as f:
3    data = f.read()
4key = b'WelcomeToQWB2023'
5rc4 = ARC4.new(key)
6with open('License_decry.dat', 'wb') as f:
7    f.write(rc4.decrypt(data))

接着随便写个 C# 读取 License_decry.dat

 1using System;
 2using System.IO;
 3using System.Security.Cryptography;
 4using System.Text;
 5namespace ConsoleApp1
 6{
 7    class Program
 8    {
 9        static void Main(string[] args)
10        {
11            byte[] data = File.ReadAllBytes("License_decry.dat");
12            string str = Encoding.UTF8.GetString(data);
13            Console.WriteLine(str);
14        }
15    }
16}

发现报错,应该是需要修复
2023-12-22T175437.png
这一段数据前一段 0 字节长度为 0x15,后一段长度为 0x10
应该是 TEA 加密的数据,长度为 0x15 的数据是 enc,长度为 0x10 的数据是 key
根据 FFF 函数解一下 TEA
2023-12-22T180819.png

 1import struct
 2v28 = bytes([
 3		69, 182, 171, 33, 121, 107, 254, 150, 92, 29, 4, 178, 138, 166, 184, 106, 53, 241, 42, 191, 23, 211, 3, 107])
 4key = struct.unpack('<4I', b'WelcomeToQWB2023')
 5delta = 3735928559
 6flag = b''
 7for i in range(0, len(v28), 8):
 8    sum = (32*delta) & 0xFFFFFFFF
 9    v0, v1 = struct.unpack_from('<2I', v28, i)
10    for k in range(32):
11        v1 -= ((v0 << 4) + key[2] ^ v0 + sum ^ (v0 >> 5) + key[3])
12        v1 &= 0xFFFFFFFF
13        v0 -= ((v1 << 4) + key[0] ^ v1 + sum ^ (v1 >> 5) + key[1])
14        v0 &= 0xFFFFFFFF
15        sum -= delta
16        sum &= 0xFFFFFFFF
17    flag += struct.pack('<2I', v0, v1)
18print(flag.decode())
19# dotN3t_Is_1nt3r3sting

填入 License_decry.dat 修复一下
0x15 和 0x10 是长度
2023-12-22T182329.png
最后由 MD5 求出 flag

 1# 将缺失的数据填入 License_decry.dat
 2with open('License_decry.dat', 'rb') as f:
 3    data = list(f.read())
 4    data[658] = 21
 5    data[659:680] = list(b'dotN3t_Is_1nt3r3sting')
 6    data[685] = 16
 7    data[686:702] = list(b'WelcomeToQWB2023')
 8with open('License_decry.dat', 'wb') as f:
 9    f.write(bytes(data))
10# MD5 解密出 flag
11h = md5(open("License_decry.dat", "rb").read()).digest()
12v10 =[59, 65, 108, 110, 223, 90, 245, 226, 6, 122, 219, 170, 147, 176, 22, 190, 60, 24, 58, 86, 150, 97, 188, 166, 113, 104, 232, 197, 234, 225, 22, 183, 40, 78, 102, 116]
13for i in range(len(v10)):
14    v10[i] ^= h[i % len(h)]
15print(bytes(v10).decode())
16# flag{d0tN3t_I5_Ea57_2_y09!G00d_Luck}
 1from Crypto.Cipher import AES
 2from Crypto.Cipher import ARC4
 3import struct
 4from hashlib import md5
 5# AES 解密出输入作为 key
 6enc = [97, 147, 49, 123, 248, 150, 224, 0, 165, 39, 183, 55, 74, 227, 3, 168]
 7key = b'QWB2023HappyGame'
 8aes = AES.new(key, AES.MODE_ECB)
 9input = aes.decrypt(bytes(enc)).decode()
10print(input)
11# WelcomeToQWB2023
12# ARC4 解密 License.dat
13with open('License.dat', 'rb') as f:
14    data = f.read()
15key = b'WelcomeToQWB2023'
16rc4 = ARC4.new(key)
17with open('License_decry.dat', 'wb') as f:
18    f.write(rc4.decrypt(data))
19# TEA 解密出缺失的数据
20v28 = bytes([
21		69, 182, 171, 33, 121, 107, 254, 150, 92, 29, 4, 178, 138, 166, 184, 106, 53, 241, 42, 191, 23, 211, 3, 107])
22key = struct.unpack('<4I', b'WelcomeToQWB2023')
23delta = 3735928559
24flag = b''
25for i in range(0, len(v28), 8):
26    sum = (32*delta) & 0xFFFFFFFF
27    v0, v1 = struct.unpack_from('<2I', v28, i)
28    for k in range(32):
29        v1 -= ((v0 << 4) + key[2] ^ v0 + sum ^ (v0 >> 5) + key[3])
30        v1 &= 0xFFFFFFFF
31        v0 -= ((v1 << 4) + key[0] ^ v1 + sum ^ (v1 >> 5) + key[1])
32        v0 &= 0xFFFFFFFF
33        sum -= delta
34        sum &= 0xFFFFFFFF
35    flag += struct.pack('<2I', v0, v1)
36print(flag.decode())
37# dotN3t_Is_1nt3r3sting
38# 将缺失的数据填入 License_decry.dat
39with open('License_decry.dat', 'rb') as f:
40    data = list(f.read())
41    data[658] = 21
42    data[659:680] = list(b'dotN3t_Is_1nt3r3sting')
43    data[685] = 16
44    data[686:702] = list(b'WelcomeToQWB2023')
45with open('License_decry.dat', 'wb') as f:
46    f.write(bytes(data))
47# MD5 解密出 flag
48h = md5(open("License_decry.dat", "rb").read()).digest()
49v10 =[59, 65, 108, 110, 223, 90, 245, 226, 6, 122, 219, 170, 147, 176, 22, 190, 60, 24, 58, 86, 150, 97, 188, 166, 113, 104, 232, 197, 234, 225, 22, 183, 40, 78, 102, 116]
50for i in range(len(v10)):
51    v10[i] ^= h[i % len(h)]
52print(bytes(v10).decode())
53# flag{d0tN3t_I5_Ea57_2_y09!G00d_Luck}

flag{d0tN3t_I5_Ea57_2_y09!G00d_Luck}

unname

java 层面的反调试,用 jadx 打开,没发现什么逻辑,
只能看到调用了 native 层的 check 函数
so 文件里 check 函数挺长的,调试一下
找到关键运算
2023-12-25T222532.png
label_147 是最终的判断
计算一下
2023-12-24T213230.png

 1from z3 import *
 2
 3solver = Solver()
 4# 使用BitVec而不是Int,并指定位数
 5x1 = BitVec('x1', 64)
 6x2 = BitVec('x2', 64)
 7x3 = BitVec('x3', 64)
 8x4 = BitVec('x4', 64)
 9
10solver.add((x1 + 0x5474374041455247) ^ 0x6835B4293DD0D39E == 0)
11solver.add((x4 - 0x7DC131EF140E7742) ^ 0xE69C68D3BC875A19 == 0)
12solver.add((x3 - 0x452C699C4F4C522D) ^ 0x1B69DAF30AE1351F == 0)
13solver.add((x2 + 0x6523745F644E5642) ^ 0xACA0DA795EF62809 == 0)
14
15if solver.check() == sat:
16    model = solver.model()
17    for i in model:
18        # 使用int()函数将BitVecNumRef转换为整数
19        print(i, hex(int(model[i].as_long())))
20# x3 0x6096448f5a2d874c
21# x2 0x477d6619faa7d1c7
22# x1 0x13c17ce8fc8b8157
23# x4 0x645d9ac2d095d15b

通过 ida/plugins/defs.h 查看 ROR8 定义
2023-12-25T220302.png
2023-12-25T220701.png2023-12-25T221021.png

 1unsigned long long __ROR8__(unsigned long long val, unsigned int shift){
 2    shift = shift % 64; 
 3    if (shift == 0) return val;
 4    return (val >> shift) | (val << (64 - shift));
 5}
 6void vaddq_s64(unsigned long long* a, unsigned long long* b, unsigned long long* res){
 7    res[0] = a[0] + b[0]; res[1] = a[1] + b[1];
 8}
 9
10unsigned long long vaddvq_s64(unsigned long long* a){
11    return a[0] + a[1];
12}

模拟 while 循环的加密过程

  1#include <stdio.h>
  2
  3unsigned long long ptr[80] = {
  4 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455247,
  5 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6366,
  6 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E71,
  7 0x5474374041455247, 0x823ECE10EBF188BE, 0xBAD39663B0B3ADD3, 0x6523745F644E5633,
  8 0x33E95EAA8C9B6365, 0xBAD39663B0B3ADD3, 0x9F44A2B46C50D06D, 0x5F30535F59333363,
  9 0x465F5530595F4E6F, 0x9F44A2B46C50D06D, 0xAD85C2C5B88958B8, 0x547437404145524C,
 10 0x6523745F644E5630, 0xAD85C2C5B88958B8, 0xC8E878739899B1AB, 0x33E95EAA8C9B636B,
 11 0x5F30535F5933335F, 0xC8E878739899B1AB, 0x6E0A8CFF949DDDA2, 0x465F5530595F4E76,
 12 0x5474374041455247, 0x6E0A8CFF949DDDA2, 0x94B4C496B8B573C8, 0x6523745F644E5638,
 13 0x33E95EAA8C9B6365, 0x94B4C496B8B573C8, 0xD997B592BBA2B594, 0x5F30535F59333368,
 14 0x465F5530595F4E6F, 0xD997B592BBA2B594, 0x995181B46135AD9C, 0x5474374041455251,
 15 0x6523745F644E5630, 0x995181B46135AD9C, 0xA2C9A6A6A09B77A0, 0x33E95EAA8C9B6370,
 16 0x5F30535F5933335F, 0xA2C9A6A6A09B77A0, 0xA85D9FDDE3EFC2C9, 0x465F5530595F4E7B,
 17 0x5474374041455247, 0xA85D9FDDE3EFC2C9, 0x808083856161C8AC, 0x6523745F644E563D,
 18 0x33E95EAA8C9B6365, 0x808083856161C8AC, 0xB378E3C5C3A47B89, 0x5F30535F5933336D,
 19 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455256,
 20 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6375,
 21 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E80,
 22 0x0000007A414B22C2, 0x0000000000000028, 0x0000000000000000, 0xB400007B596C6C00,
 23 0xB400007A99ABAC40, 0x0000000000000010, 0x0000000000000010, 0x0000000000000001
 24};
 25
 26//x1, x2, x3, x4最终的结果
 27unsigned long long res[4] = {
 28    0x3c17ce8fc8b8157, 0x477d6619faa7d1c7, 0x6096448f5a2d874c,0x645d9ac2d095d15b
 29};
 30
 31unsigned char ror_num[16] = {
 32    0x0E, 0x10, 0x34, 0x39, 0x17, 0x28, 0x05, 0x25, 0x19, 0x21, 0x2E, 0x0C, 0x3A, 0x16, 0x20, 0x20
 33};
 34
 35unsigned long long __ROR8__(unsigned long long val, unsigned int shift){
 36    shift = shift % 64; 
 37    if (shift == 0) return val;
 38    return (val >> shift) | (val << (64 - shift));
 39}
 40void vaddq_s64(unsigned long long* a, unsigned long long* b, unsigned long long* res){
 41    res[0] = a[0] + b[0]; res[1] = a[1] + b[1];
 42}
 43
 44unsigned long long vaddvq_s64(unsigned long long* a){
 45    return a[0] + a[1];
 46}
 47
 48int main(){
 49    unsigned long long input[4] = {
 50        0x3c17ce8fc8b8157, 0x477d6619faa7d1c7, 0x6096448f5a2d874c,0x645d9ac2d095d15b
 51    };
 52    int v49 = 0;
 53    int v61 = 0;
 54    unsigned char * v52, * v62;
 55    unsigned long long* v53;
 56    unsigned long long v54[2] = { 0 };
 57    unsigned long long * v51 = &input[2];
 58    unsigned long long v55, v58, v63;
 59    unsigned long long x1, x2, x3, x4;
 60    do {
 61        while (1){
 62            v61 = v49 % 8;
 63            if (v49 % 4 != 0){
 64                break;
 65            }
 66            v52 = &ror_num[2 * v61];
 67            v53 = &ptr[4 * (v49 >> 2)];
 68            vaddq_s64(v53+2, &input[2], v54);
 69            v55 = v53[1] + input[1];
 70            x1 = v53[0] + input[0] + v55;
 71            x2 = __ROR8__(v55, 64 - v52[0]) ^ x1;
 72            input[0] = x1;
 73            v58 = __ROR8__(v54[1], 64 - v52[1]);
 74            v51[1] = x2;
 75            x3 = vaddvq_s64(v54);
 76            x4 = v58 ^ x3;
 77            input[1] = x4;
 78            v49++;
 79            v51[0] = x3;
 80            printf("v49: %d\n", v49);
 81            printf("0x%llX\n", x1);
 82            printf("0x%llX\n", x2);
 83            printf("0x%llX\n", x3);
 84            printf("0x%llX\n", x4);
 85            printf("-----------------------------------\n");
 86            if (v49 == 72)
 87                goto LABEL_147;
 88        }
 89        v62 = &ror_num[2 * v61];
 90        x1 = input[1] + input[0];
 91        input[0] = x1;
 92        x2 = __ROR8__(input[1], 64 - v62[0]) ^ x1;
 93        v51[0] = vaddvq_s64(v51);
 94        v63 = __ROR8__(v51[1], 64 - v62[1]);
 95        x3 = v51[0];
 96        v51[1] = x2;
 97        x4 = v63 ^ v51[0];
 98        input[1] = x4;
 99        ++v49;
100        printf("v49: %d\n", v49);
101        printf("0x%llX\n", x1);
102        printf("0x%llX\n", x2);
103        printf("0x%llX\n", x3);
104        printf("0x%llX\n", x4);
105        printf("-----------------------------------\n");
106    } while (v49 != 72);
107
108LABEL_147:
109    printf("end\n");
110    printf("0x%llX\n", x1);
111    printf("0x%llX\n", x2);
112    printf("0x%llX\n", x3);
113    printf("0x%llX\n", x4);
114    return 0;
115}

逆一下

 1#include <stdio.h>
 2
 3unsigned long long ptr[80] = {
 4 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455247,
 5 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6366,
 6 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E71,
 7 0x5474374041455247, 0x823ECE10EBF188BE, 0xBAD39663B0B3ADD3, 0x6523745F644E5633,
 8 0x33E95EAA8C9B6365, 0xBAD39663B0B3ADD3, 0x9F44A2B46C50D06D, 0x5F30535F59333363,
 9 0x465F5530595F4E6F, 0x9F44A2B46C50D06D, 0xAD85C2C5B88958B8, 0x547437404145524C,
10 0x6523745F644E5630, 0xAD85C2C5B88958B8, 0xC8E878739899B1AB, 0x33E95EAA8C9B636B,
11 0x5F30535F5933335F, 0xC8E878739899B1AB, 0x6E0A8CFF949DDDA2, 0x465F5530595F4E76,
12 0x5474374041455247, 0x6E0A8CFF949DDDA2, 0x94B4C496B8B573C8, 0x6523745F644E5638,
13 0x33E95EAA8C9B6365, 0x94B4C496B8B573C8, 0xD997B592BBA2B594, 0x5F30535F59333368,
14 0x465F5530595F4E6F, 0xD997B592BBA2B594, 0x995181B46135AD9C, 0x5474374041455251,
15 0x6523745F644E5630, 0x995181B46135AD9C, 0xA2C9A6A6A09B77A0, 0x33E95EAA8C9B6370,
16 0x5F30535F5933335F, 0xA2C9A6A6A09B77A0, 0xA85D9FDDE3EFC2C9, 0x465F5530595F4E7B,
17 0x5474374041455247, 0xA85D9FDDE3EFC2C9, 0x808083856161C8AC, 0x6523745F644E563D,
18 0x33E95EAA8C9B6365, 0x808083856161C8AC, 0xB378E3C5C3A47B89, 0x5F30535F5933336D,
19 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455256,
20 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6375,
21 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E80,
22 0x0000007A414B22C2, 0x0000000000000028, 0x0000000000000000, 0xB400007B596C6C00,
23 0xB400007A99ABAC40, 0x0000000000000010, 0x0000000000000010, 0x0000000000000001
24};
25
26//x1, x2, x3, x4最终的结果
27unsigned long long res[4] = {
28    0x3c17ce8fc8b8157, 0x477d6619faa7d1c7, 0x6096448f5a2d874c,0x645d9ac2d095d15b
29};
30
31unsigned char ror_num[16] = {
32    0x0E, 0x10, 0x34, 0x39, 0x17, 0x28, 0x05, 0x25, 0x19, 0x21, 0x2E, 0x0C, 0x3A, 0x16, 0x20, 0x20
33};
34
35unsigned long long __ROR8__(unsigned long long val, unsigned int shift){
36    shift = shift % 64; 
37    if (shift == 0) return val;
38    return (val >> shift) | (val << (64 - shift));
39}
40void vaddq_s64(unsigned long long* a, unsigned long long* b, unsigned long long* res){
41    res[0] = a[0] + b[0]; res[1] = a[1] + b[1];
42}
43
44unsigned long long vaddvq_s64(unsigned long long* a){
45    return a[0] + a[1];
46}
47
48int main(){
49    unsigned long long input[4] = { 0 };
50    int v49 = 72;
51    int v61 = 0;
52    unsigned char * v52, * v62;
53    unsigned long long* v53;
54    unsigned long long v54[2] = { 0 };
55    unsigned long long * v51 = &input[2];
56    unsigned long long v55, v58, v63;
57    unsigned long long x1 = 0x13c17ce8fc8b8157, x2 = 0x477d6619faa7d1c7, x3 = 0x6096448f5a2d874c, x4 = 0x645d9ac2d095d15b;
58    while (v49 >= 1){
59        --v49;
60        v61 = v49 % 8;
61        if (v49 % 4 != 0){
62            v62 = &ror_num[2 * v61];
63            input[1] = x4;
64            v51[1] = x2;
65            v51[0] = x3;
66            v63 = v51[0] ^ x4;
67            v51[1] = __ROR8__(v63, v62[1]);
68            v51[0] = v51[0] - v51[1];
69            input[1] = __ROR8__(x1 ^ x2, v62[0]);
70            input[0] = x1 - input[1];
71        }else {
72            v52 = &ror_num[2 * v61];
73            v53 = &ptr[4 * (v49 >> 2)];
74            v51[0] = x3;
75            input[1] = x4;
76            v58 = x3 ^ x4;
77            v51[1] = x2;
78            v54[1] = __ROR8__(v58, v52[1]);
79            v54[0] = x3 - v54[1];
80            input[0] = x1;
81            v55 = __ROR8__(x1 ^ x2, v52[0]);
82            input[0] = x1 - v55 - v53[0];
83            input[1] = v55 - v53[1];
84            unsigned long long* v53_2 = v53 + 2;
85            input[2] = v54[0] - v53_2[0];
86            input[3] = v54[1] - v53_2[1];
87        }
88        x1 = input[0];
89        x4 = input[1];
90        x3 = input[2];
91        x2 = input[3];
92    }
93    printf("%s\n", (char*)input);
94    return 0;
95}
96// flag{7hIs_I$_nEw_Try1N9_@cu7U@1}0@@

flag{7hIs_I$_nEw_Try1N9_@cu7U@1}

xrtFuze

VM 题,又臭又长
先锁定到 flag 格式 长度为 22
image.png
从 main 函数一路跟进到 VM 解释器 sub_8048863
大致看一下 VM 解释器里面的操作,有一些 << 和 >> 可以大胆猜测是 Tea 上调试验证也行。
下硬件断点调试,可以发现输入先和常量异或,然后转成大端序 4 字节,然后进行 Tea 加密。
通过在每一条操作和条件判断的 opcode 上加条件断点,输出操作和数据,从而观察到有大量的异或操作的垃圾指令
而我们只需要寻找 Tea 的常量,通过 Trace 跟踪和分析内存得到以下几个量,同时在密文比较处下断点,得到密文

1xorkey = [
20xF3, 0x2F, 0x51, 0x1B, 0x04, 0xFC, 0xCE, 0xA9, 0x1B, 0xEC, 0x40, 0x42, 0xEA, 0x8A, 0xD0, 0x7A]
3delta = 0x57429F48
4rounds = 32
5k0 = 0x23575896
6k1 = 0x89654528
7k3 = 0x12582548 
8k4 = 0x45897856
9enc = [0x9C, 0x6C, 0x48, 0x16, 0x70, 0x12, 0x5A, 0x2D, 0xE1, 0xD7, 0xF5, 0x7C, 0xC5, 0x46, 0x32, 0x68]
 1#include <stdio.h>
 2#include <stdint.h>
 3
 4#define delta 0x57429f48
 5
 6void encrypt(uint32_t *v, uint32_t *k) {
 7    uint32_t v0 = v[0], v1 = v[1], sum = 0;
 8    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
 9    for (int i = 0; i < 32; i++) {
10        sum += delta;
11        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ (((int32_t)v1 >> 5) + k1);
12        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ (((int32_t)v0 >> 5) + k3);
13    }
14    v[0] = v0; v[1] = v1;
15}
16
17void decrypt(uint32_t *v, uint32_t *k) {
18    uint32_t v0 = v[0], v1 = v[1], sum = delta * 32;
19    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
20    for (int i = 0; i < 32; i++) {
21        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ (((int32_t)v0 >> 5) + k3);
22        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ (((int32_t)v1 >> 5) + k1);
23        sum -= delta;
24    }
25    v[0] = v0; v[1] = v1;
26}
27
28int main() {
29    uint8_t enc[17] = {0x16, 0x48, 0x6C, 0x9c, 0x2d, 0x5a, 0x12, 0x70, 0x7c, 0xf5, 0xd7, 0xe1, 0x68, 0x32, 0x46, 0xc5, 0};
30    uint32_t k[4] = {0x23575896, 0x89654528, 0x12582548, 0x45897856};
31    for (size_t i = 0; i < 16; i += 8) {
32        decrypt((uint32_t*)(enc + i), k);
33    }
34    // 小端转大端
35    for (size_t i = 0; i < 16; i += 4) {
36        uint32_t tmp = *(uint32_t*)(enc + i);
37        *(uint32_t*)(enc + i) = ((tmp & 0xff) << 24) | ((tmp & 0xff00) << 8) | ((tmp & 0xff0000) >> 8) | ((tmp & 0xff000000) >> 24);
38    }
39    uint8_t xor_key[16] = {0x1b, 0x51, 0x2f, 0xf3, 0xa9, 0xce, 0xfc, 0x4, 0x42, 0x40, 0xec, 0x1b, 0x7a, 0xd0, 0x8a, 0xea};
40    for (size_t i = 0; i < 16; i++) {
41        enc[i] ^= xor_key[i];
42    }
43    printf("%s\n", enc);
44
45    return 0;
46}
47//flag{1fu13_n19ela06~!}

题型分析

强网先锋

babyre

主要考动态调试,简单的 BeingDebugged 的绕过。加密算法是一个 Tea 的识别,同时注意 Tea 的魔改就行了

ezre

OLLVM 混淆控制流平坦化,不知道为什么这次好多题都涉及到了~~(没活整了吗) ~~ 脚本去平坦化后简单的动调获取 base64Table,逆就完了

Reverse

ezre

OLLVM 混淆控制流平坦化,考验代码阅读快速定位可疑加密函数的能力,很考验锁定关键函数的技巧。同时结合插件识别出 SM4 加密。最好是不借助插件也能一眼识别出来 SM4。

dotdot

.NET 逆向,同时考验工具的选择,dnspy 少了数据,ILSpy 就比较完美。后面还听说 VS 也能用,感兴趣可以试试。
主体是一个白盒 AES 的解密,然后 RC4 解密 License.dat , 最后经典的和出题人对脑电波环节来修复 License.dat

unname

apk 逆向,主要考 native 层的逆向,同时考验几个 ida 自带函数的定义,同时加密逻辑比较长比较乱,考验用自己的代码能力重构模拟加密过程,最后进行逆向的过程。

xrtFuze

主要考查 VM 逆向,核心是读懂 opcode 对应的操作,一般关键都在 VM 解释器中,搞清楚 VM 解释器,基本上 VM 题也就解得差不多了。VM 题往往很考验耐心与细心。这题同时也考察了下断点和调试的技巧。

附件下载

RE 题目附件

http://47.107.252.179:6371/home/file/share/6b2daabbbe6df347e5dadfbd881c9212

RE 工具附件

http://47.107.252.179:6371/home/file/share/2cc7b1face45419605910bd7c9e923fd

HnuSec

Are you still in pain?

文章目录