赛题解析
强网先锋
babyre
主函数分析如下
同时注意到有 TLS 反调试
TLS 反调试里修改了数据,考虑动调获取数据
先绕过两个 BeingDebugged
运行到 jz 跳转指令处,修改 ZF 标志为 0
下一个 jz 跳转指令处,修改 ZF 标志为 0
此时再 F9 运行,提示进到主函数里
随便输入长度为 32 的字符串,此时 IDA 进入了 TEA 函数
TEA 函数 IDA 分析有点问题
看看汇编
可以看到实际的 delta 值为 0x88408067
我们可以通过计算获取到解密脚本需要的 sum 值
也可以顺手在该函数中获取一下,运行第二个断点 return 处
查看一下 v4 的值 0xD192C263
最后回到主函数获取一下经过修改的 key 和 enc
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 梭
主函数如下
先看最后一步 sub_401EB0
先逆一下,动调取出 base64_Table
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
解码
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 梭
主函数如下
没什么有价值信息,只在 sub_3180 里看到了假 flag
函数不多一个个找,发现 sub_3580 里有个可疑数据,长得很像加密过程
而且调用了 sub_2220 可以看出来是 SM4 加密 signsrch 也识别出来了
直接 cyberchef 梭
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
whitebox-aes
解出来主密钥为 QWB2023HappyGame
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}
发现报错,应该是需要修复
这一段数据前一段 0 字节长度为 0x15,后一段长度为 0x10
应该是 TEA 加密的数据,长度为 0x15 的数据是 enc,长度为 0x10 的数据是 key
根据 FFF 函数解一下 TEA
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 是长度
最后由 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 函数挺长的,调试一下
找到关键运算
label_147 是最终的判断
计算一下
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 定义
即
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
从 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