# 2025N1CTF 逆向 wp

www 赛中解出两道,赛后一道,总体难度还是适中的_

# n1vm

程序接收四个输入,其中 sub_140001360 是主要加密函数,打开发现进行了轻微混淆

这个 lea + push + retn 明显是一个模拟 jmp 的操作,可以写 idc 脚本匹配特征然后计算偏移 patch 成 jmp,去除后可以成功反编译,但是伪代码非常难看,可以发现另一种混淆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:0000000140001CBD                 mov     r11, [r8+18h]
.text:0000000140001CC1 test r11, 1
.text:0000000140001CC8 pushfq
.text:0000000140001CC9 pop r11
.text:0000000140001CCB mov [r8+80h], r11
.text:0000000140001CD2 lea rbx, loc_140001A82
.text:0000000140001CD9 push rbx
.text:0000000140001CDA retn
.text:0000000140001A82 loc_140001A82: ; DATA XREF: sub_1400015E2+6F0↓o
.text:0000000140001A82 push qword ptr [r8+80h]
.text:0000000140001A89 popfq
.text:0000000140001A8A jz loc_14000162F
.text:0000000140001A90 lea r14, loc_140001A9A
.text:0000000140001A97 push r14
.text:0000000140001A99 retn

这边将 test r11,1 的结果放到 r11 寄存器中,然后又把 r11 压栈,再从栈中恢复 RFLAG,也就是说从 pushfqpopfq 全都是无用操作,写出去混淆脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <idc.idc>

static NopCode(Addr, Length)
{
auto i;
for (i = 0; i < Length; i++)
{
PatchByte(Addr + i, 0x90);
}
}


static main()
{
auto seg;
for (seg = get_first_seg(); seg != BADADDR; seg = get_next_seg(seg))
{
auto seg_name = get_segm_name(seg);
msg(seg_name + "\n");
if (seg_name == ".text")
{
auto current_addr = seg;
auto end_addr = current_addr + 0x10000;
while (current_addr != BADADDR && current_addr < end_addr)
{

auto insn_name = print_insn_mnem(current_addr);
auto ret_byte = Byte(current_addr + 7);
auto current_byte = Byte(current_addr);
if(insn_name == "lea" && ret_byte == 0xC3)
{
auto offest = Dword(current_addr + 3) + 2;
NopCode(current_addr,9);
PatchByte(current_addr,0xE9);
PatchDword(current_addr + 1,offest);
current_addr = offest + 5;
}

if(current_byte == 0x9c)
{
NopCode(current_addr,10);
}

if(current_byte == 0x9D)
{
NopCode(current_addr - 7,8);
}
current_addr = next_head(current_addr, end_addr);
}

}
}
}

去掉之后程序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
void __fastcall sub_7FF6050E1360()
{
__int16 v0; // r10
_QWORD *v1; // r13
_QWORD *v2; // r11
_QWORD *v3; // r11
unsigned __int64 v4; // rdx
_QWORD *v5; // r9
char v6; // al
__int64 v7; // r12
char *v8; // rbx
__int64 v9; // rbp
_UNKNOWN *retaddr; // [rsp+0h] [rbp+0h] BYREF
__int64 v11; // [rsp+8h] [rbp+8h] BYREF

rsp_main = &retaddr;
v9 = (__int64)&v11;
ptr3 = 0;
qword_7FF6050E6670 = (unsigned __int64 *)((char *)&code - 15040);
LOBYTE(v1) = 0;
res = 0;
ptr4 = 0;
ptr1 = 0;
do
{
if ( ((unsigned __int8)ptr2 & 1) != 0 )
{
res = (_QWORD *)((char *)res + *(unsigned __int64 *)((char *)qword_7FF6050E6670 + ptr1 + 15040));// code[ptr1]
v1 = (unsigned __int64 *)((char *)qword_7FF6050E6670 + ptr1 + 12992);// data[ptr1]
v9 = 653;
ptr4 += *v1;
if ( (unsigned __int64)res > 0x1770 )
goto wrong;
}
v9 += 292;
ptr2 = (_QWORD *)((unsigned __int64)ptr2 >> 1);
v4 = ptr1 + 8LL;
v0 = v0 & 0x235 ^ 0x31D;
ptr1 += 8LL;
v7 = ptr1;
}
while ( ptr1 < 0x200u );
ptr2 = ptr3;
do
{
if ( (input_2222[0] & 1) != 0 )
{
LOBYTE(v1) = (_BYTE)qword_7FF6050E6670;
res = (_QWORD *)((char *)res + *(_QWORD *)((char *)ptr2 + (_QWORD)qword_7FF6050E6670 + 15552));// code[64 + ptr2]
v9 = *(_QWORD *)((char *)ptr2 + (_QWORD)qword_7FF6050E6670 + 13504) + ptr4;// data2[64 + ptr2]
v4 = (unsigned __int64)qword_7FF6050E6670 ^ ptr4;
ptr4 = v9;
v7 = (__int64)res;
if ( (unsigned __int64)res > 0x1770 )
goto wrong;
}
v7 *= 2;
input_2222[0] >>= 1;
v8 = (char *)(ptr2 + 1);
v9 -= v4;
++ptr2;
}
while ( (unsigned __int64)ptr2 < 0x200 );
v3 = ptr3;
ptr2 = ptr3;
do
{
if ( (qword_7FF6050E6680[0] & 1) != 0 )
{
res = (_QWORD *)((char *)res + *(_QWORD *)((char *)ptr2 + (_QWORD)qword_7FF6050E6670 + 16064));// code[128 + ptr2]
v1 = (_QWORD *)(*(_QWORD *)((char *)ptr2 + (_QWORD)qword_7FF6050E6670 + 14016) + ptr4);// data[128 + ptr2]
v8 = (char *)(2LL * (((unsigned __int16)((_WORD)ptr2 + 14016) ^ (unsigned __int16)qword_7FF6050E6670) & 0x292));
ptr4 = v1;
if ( (unsigned __int64)res > 0x1770 )
goto wrong;
}
qword_7FF6050E6680[0] >>= 1;
v6 = (_BYTE)v1 - 28;
LOBYTE(v1) = (_BYTE)ptr2 + 8;
v3 = (_QWORD *)((char *)v3 - 882);
++ptr2;
}
while ( (unsigned __int64)ptr2 < 0x200 );
do
{
if ( (qword_7FF6050E6680[1] & 1) != 0 )
{
v5 = (_QWORD *)((char *)ptr3 + (_QWORD)qword_7FF6050E6670 + 16576);
v6 = (unsigned __int8)qword_7FF6050E6670
| ((_BYTE)qword_7FF6050E6670 + (_BYTE)ptr3 - 64)
^ ((((-79 - (((unsigned __int8)res | 0x88) - ((_BYTE)ptr3 - 64))) ^ (unsigned __int8)res & (v6 | 0xE2)) & 0x79)
+ 35
- ((_BYTE)ptr3
- 64));
res = (_QWORD *)((char *)res + *v5);
v2 = (_QWORD *)((char *)ptr3 + (_QWORD)qword_7FF6050E6670 + 14528);
v8 = (char *)((ptr4 - (_QWORD)v2)
& 0x3A1LL
& (((((unsigned __int16)v5 & 0x29A) + 573LL) ^ (v8 - (char *)qword_7FF6050E6670) ^ 0x1CB)
- (*v2
+ ptr4)));
ptr4 += *v2;
if ( (unsigned __int64)res > 0x1770 )
goto wrong;
}
qword_7FF6050E6680[1] >>= 1;
++ptr3;
}
while ( (unsigned __int64)ptr3 < 0x200 );
if ( ptr4 >= 0x7B82u )
{
ptr4 = rsp_main[1];
res = (_QWORD *)1;
qword_7FF6050E6670 = (unsigned __int64 *)rsp_main[2];
}
else
{
wrong:
res = 0;
ptr4 = rsp_main[1];
qword_7FF6050E6670 = (unsigned __int64 *)rsp_main[2];
}
JUMPOUT(0x7FF6050E15BFLL);
}

四个 do-while 循环加密,稍微整理一下,把没用的代码删除,可以同构出加密代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <iostream>
#include <windows.h>
#include <stdint.h>

uint64_t code[] =
{
0x2F0, 0x328, 0x21B, 0x392, 0x0BA, 0x371, 0x04E, 0x099, 0x061, 0x380, 0x0E9, 0x1F3, 0x33C, 0x018, 0x20E, 0x033, 0x028, 0x0AA, 0x158, 0x188, 0x381, 0x331, 0x02E, 0x040, 0x1E4, 0x011, 0x1ED, 0x1E1, 0x14F, 0x204, 0x24C, 0x1DF, 0x32B, 0x1CF, 0x271, 0x2AD, 0x25F, 0x0BF, 0x0FE, 0x306, 0x11A, 0x19A, 0x1B0, 0x399, 0x33F, 0x0F1, 0x2BB, 0x0FA, 0x3A4, 0x385, 0x06B, 0x164, 0x2F6, 0x07F, 0x051, 0x223, 0x173, 0x12B, 0x3AD, 0x086, 0x106, 0x27D, 0x390, 0x201, 0x1AB, 0x1B7, 0x3C9, 0x303, 0x25A, 0x2B1, 0x2A0, 0x288, 0x142, 0x0B0, 0x1F5, 0x072, 0x3E0, 0x37F, 0x3C4, 0x285, 0x35F, 0x293, 0x2D4, 0x2A8, 0x015, 0x0F6, 0x3B6, 0x34B, 0x171, 0x2C3, 0x27B, 0x28D, 0x38C, 0x137, 0x3B0, 0x05E, 0x329, 0x265, 0x11C, 0x09D, 0x2DE, 0x024, 0x0F3, 0x34F, 0x246, 0x266, 0x133, 0x03A, 0x08E, 0x273, 0x279, 0x33E, 0x09C, 0x2F8, 0x107, 0x1AF, 0x222, 0x1A3, 0x323, 0x2FF, 0x234, 0x2AF, 0x00F, 0x1FA, 0x3D7, 0x120, 0x344, 0x3C8, 0x149, 0x25D, 0x36D, 0x01A, 0x160, 0x221, 0x05B, 0x075, 0x197, 0x275, 0x1B9, 0x31E, 0x2BC, 0x34C, 0x143, 0x3D0, 0x302, 0x103, 0x30F, 0x378, 0x32E, 0x1BB, 0x2EE, 0x2E6, 0x1C6, 0x0B6, 0x280, 0x348, 0x2FD, 0x3B3, 0x3A1, 0x1CC, 0x352, 0x128, 0x38B, 0x155, 0x354, 0x1C3, 0x1E8, 0x242, 0x244, 0x139, 0x30C, 0x1D6, 0x3BD, 0x012, 0x0B2, 0x23C, 0x1B8, 0x2FC, 0x281, 0x1D5, 0x36E, 0x077, 0x05F, 0x3DA, 0x17E, 0x05C, 0x3B8, 0x2F4, 0x0C0, 0x0AB, 0x0B7, 0x3CA, 0x335, 0x2E0, 0x09F, 0x30A, 0x1A8, 0x16F, 0x39B, 0x25C, 0x01B, 0x105, 0x0FB, 0x393, 0x257, 0x224, 0x109, 0x0CB, 0x3BA, 0x264, 0x0A3, 0x30D, 0x114, 0x3E7, 0x0EB, 0x0C5, 0x09A, 0x1AD, 0x2C1, 0x04C, 0x094, 0x2CE, 0x210, 0x330, 0x3CE, 0x1C0, 0x008, 0x26A, 0x06C, 0x2AE, 0x001, 0x156, 0x2A2, 0x29C, 0x1C1, 0x1D0, 0x388, 0x20F, 0x11F, 0x364, 0x389, 0x068, 0x058, 0x11E, 0x163, 0x373, 0x132, 0x296, 0x368, 0x045, 0x111, 0x05D, 0x231, 0x168, 0x1EC, 0x1A7
};

uint64_t data[] =
{
0x35E, 0x060, 0x203, 0x073,
0x23E, 0x19F, 0x2F9, 0x079,
0x3A6, 0x26A, 0x348, 0x3B6,
0x045, 0x286, 0x0FE, 0x2B1,
0x231, 0x1A0, 0x22D, 0x1F7,
0x37C, 0x16E, 0x3E1, 0x2AB,
0x022, 0x19B, 0x05A, 0x006,
0x191, 0x052, 0x383, 0x098,
0x08E, 0x1F1, 0x19C, 0x03D,
0x215, 0x011, 0x338, 0x061,
0x088, 0x22A, 0x115, 0x222,
0x224, 0x1C1, 0x002, 0x2FC,
0x1AB, 0x3C5, 0x0CF, 0x3BD,
0x37A, 0x1EF, 0x1DA, 0x2E8,
0x0DD, 0x198, 0x20A, 0x205,
0x081, 0x2B3, 0x2BD, 0x233,
0x173, 0x2C3, 0x1DE, 0x140,
0x144, 0x18C, 0x0F4, 0x32F,
0x0D8, 0x1E5, 0x04C, 0x341,
0x27F, 0x033, 0x211, 0x18F,
0x2C6, 0x382, 0x08A, 0x2EF,
0x2E1, 0x0AB, 0x29D, 0x0DE,
0x0E1, 0x31A, 0x390, 0x3DE,
0x1CA, 0x368, 0x276, 0x234,
0x132, 0x2B6, 0x10D, 0x080,
0x3C2, 0x0A4, 0x3DA, 0x3B0,
0x21E, 0x379, 0x078, 0x21A,
0x3CD, 0x34B, 0x384, 0x342,
0x204, 0x0F0, 0x19E, 0x11C,
0x0D9, 0x2AE, 0x3CC, 0x1E7,
0x074, 0x2C1, 0x087, 0x3BB,
0x02F, 0x369, 0x20B, 0x007,
0x372, 0x114, 0x30C, 0x189,
0x2B9, 0x18B, 0x39A, 0x23B,
0x127, 0x024, 0x057, 0x261,
0x3E7, 0x259, 0x2D5, 0x39E,
0x1A3, 0x3C3, 0x2FA, 0x13D,
0x0B1, 0x3E5, 0x27E, 0x021,
0x2E2, 0x3B1, 0x334, 0x305,
0x10B, 0x3A4, 0x0A1, 0x17C,
0x27D, 0x251, 0x187, 0x0F7,
0x37B, 0x346, 0x1D1, 0x1A1,
0x180, 0x2D6, 0x13E, 0x330,
0x2D4, 0x009, 0x3CB, 0x170,
0x0B9, 0x298, 0x06E, 0x0A7,
0x1FE, 0x2DE, 0x25D, 0x34A,
0x09E, 0x175, 0x093, 0x3CE,
0x30E, 0x1C2, 0x2E3, 0x12A,
0x02D, 0x2F2, 0x2E9, 0x1A8,
0x13C, 0x3C9, 0x08D, 0x36E,
0x27C, 0x26E, 0x337, 0x393,
0x280, 0x26F, 0x0EB, 0x387,
0x121, 0x26B, 0x183, 0x16F,
0x065, 0x16A, 0x092, 0x06A,
0x36F, 0x14E, 0x1FC, 0x0AE,
0x169, 0x31F, 0x18E, 0x0D4,
0x295, 0x099, 0x14B, 0x109,
0x134, 0x0A0, 0x0ED, 0x1EA,
0x2D9, 0x111, 0x333, 0x263,
0x2E6, 0x188, 0x055, 0x2F8,
0x117, 0x1DB, 0x1AF, 0x0E7,
0x296, 0x2A7, 0x2F1, 0x018,
0x154, 0x05D, 0x17E, 0x0DA,
0x2A9, 0x0FC, 0x0CD, 0x289
};

uint64_t input[4] = {0x0860804002c1a550,0x2401186080100a00,0x52604000020200c9,0x0006005401008504};

int main()
{

uint64_t res = 0;
uint64_t ptr4 = 0;

for (int i = 0; i < 64; ++i) {
if (input[0] & (1ULL << i)) {
res += code[i];
ptr4 += data[i];
if (res > 0x1770) goto wrong;
}
}


for (int i = 0; i < 64; ++i) {
if (input[1] & (1ULL << i)) {
res += code[64 + i];
ptr4 += data[64 + i];
if (res > 0x1770) goto wrong;
}
}


for (int i = 0; i < 64; ++i) {
if (input[2] & (1ULL << i)) {
res += code[128 + i];
ptr4 += data[128 + i];
if (res > 0x1770) goto wrong;
}
}


for (int i = 0; i < 64; ++i) {
if (input[3] & (1ULL << i)) {
res += code[192 + i];
ptr4 += data[192 + i];
if (res > 0x1770) goto wrong;
}
}

if (ptr4 >= 0x7B82) {
printf("great");
} else {
wrong:
printf("wrong");
}

}

然后交给 gpt 一把梭了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
code = [
0x2F0, 0x328, 0x21B, 0x392, 0x0BA, 0x371, 0x04E, 0x099, 0x061, 0x380, 0x0E9, 0x1F3, 0x33C, 0x018, 0x20E, 0x033, ......
0x389, 0x068, 0x058, 0x11E, 0x163, 0x373, 0x132, 0x296, 0x368, 0x045, 0x111, 0x05D, 0x231, 0x168, 0x1EC, 0x1A7
]

data = [
0x35E, 0x060, 0x203, 0x073,
......
]

LIMIT = 0x1770
TARGET = 0x7B82
N = len(code)
assert N == 256

dp_cur = [-1] * (LIMIT + 1)
dp_cur[0] = 0

choice = [bytearray((LIMIT+1)) for _ in range(N)]

for idx in range(N):
cost = int(code[idx])
val = int(data[idx])
dp_next = dp_cur[:]
if cost <= LIMIT:
for c in range(LIMIT, cost - 1, -1):
prev_c = c - cost
if dp_cur[prev_c] != -1:
newv = dp_cur[prev_c] + val
if newv > dp_next[c]:
dp_next[c] = newv
choice[idx][c] = 1
dp_cur = dp_next

best_val = max(dp_cur)
best_cs = [c for c, v in enumerate(dp_cur) if v == best_val]
best_c = min(best_cs)

print("最佳 cost =", best_c, "达到的 data 总和 =", best_val)
print("是否达到 TARGET (>=31650)?", best_val >= TARGET)


selected = []
c = best_c
for idx in range(N-1, -1, -1):
if c < 0:
break
if choice[idx][c]:
selected.append(idx)
c -= code[idx]

selected.sort()
print("选中索引 count =", len(selected))
print("选中索引列表 =", selected)
total_cost = sum(code[i] for i in selected)
total_data = sum(data[i] for i in selected)
print("验证: total cost =", total_cost, " total data =", total_data)

inputs = [0,0,0,0]
for i in selected:
block = i // 64
pos = i % 64
inputs[block] |= (1 << pos)

for j in range(4):
print(f"input[{j}] = 0x{inputs[j]:016x}")

把给出的四个值交到程序里就可以了

# True Operator Master

根据字符串定位到主逻辑

开头和结尾都进行了一个乱序操作,中间加密非常像 tea,但是进行了混淆。

大致看了一下,有两种混淆,cmp + 跳转以及花指令

花指令比较好去除,但是 cmp 这个混淆是一个大的跳转表,同一个跳转表很多地方都会用到,所以很难去,不过可以注意到里面有很多单独包装的计算函数

注意这些计算函数里有花指令,藏了反调试

本来是想在计算处下断然后 trace 数据流,但是发现出题人似乎是把 tea 展开了,里面有非常多的计算函数,同一个运算就有几百上千个函数实现,这种情况不可能每个都下断,所以尝试解析汇编使用 idc 脚本自动下断点

然后再通过每个计算函数的上下文进行一个约束,保证我 i 们 trace 出来的是计算函数里的数据,而不是其他地方的

比如这里, div 指令后面是 mov rax,rdx 这条指令,就可以根据这个特征去匹配程序里所有的 div

脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include <idc.idc>

static SetBptBreak(Address, Enable)
{
auto OldFlag = get_bpt_attr(Address, BPTATTR_FLAGS);
if (Enable == 1)
{
if ((OldFlag & BPT_BRK) == 0)
{
msg("%d\n", OldFlag & BPT_BRK);
OldFlag = OldFlag | BPT_BRK;
}
}
else
{
if ((OldFlag & BPT_BRK) != 0)
{
OldFlag = OldFlag & (~BPT_BRK);
}
}
set_bpt_attr(Address, BPTATTR_FLAGS, OldFlag);
}


static SetHandlerToBpt(Address, HandlerFuncName, IsBreak)
{
auto Cond = sprintf("return %s();", HandlerFuncName);
if (check_bpt(Address) == (BPTCK_NONE))
{
add_bpt(Address);
}

SetBptBreak(Address, IsBreak);

auto Status = set_bpt_cond(Address, Cond);
if (Status == 1)
{
msg("Successfully set conditional bpt at 0x%x -> call %s\n", Address, HandlerFuncName);
}
else
{
msg("Failed to set conditional bpt at 0x%x\n", Address);
}
}

static Handler1()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rdx = GetRegValue("rdx");
fprintf(fp, "%X ^ %X = ",rax,rdx);
fclose(fp);
return 0;
}

static Handler2()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rdx = GetRegValue("rbx");
fprintf(fp, "%X / %X = ",rax,rdx);
fclose(fp);
return 0;
}

static Handler3()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rdx = GetRegValue("rdx");
fprintf(fp, "%X | %X = ",rax,rdx);
fclose(fp);
return 0;
}

static Handler4()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rdx = GetRegValue("rdx");
fprintf(fp, "%X - %X = ",rax,rdx);
fclose(fp);
return 0;
}

static Handler5()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rcx = GetRegValue("rcx") & 0xFF;
fprintf(fp, "%X << %X = ",rax,rcx);
fclose(fp);
return 0;
}

static Handler6()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rdx = GetRegValue("rdx");
fprintf(fp, "%X & %X = ",rax,rdx);
fclose(fp);
return 0;
}

static Handler7()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rcx = GetRegValue("rcx") & 0xFF;
fprintf(fp, "%X >> %X = ",rax,rcx);
fclose(fp);
return 0;
}

static Handler8()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
fprintf(fp, "%X\n",rax);
fclose(fp);
return 0;
}

static Handler9()
{
auto fp = fopen("trace.txt", "a");
auto rax = GetRegValue("rax");
auto rdx = GetRegValue("rdx");
fprintf(fp, "%X + %X = ",rax,rdx);
fclose(fp);
return 0;
}

static main()
{
auto seg;
for (seg = get_first_seg(); seg != BADADDR; seg = get_next_seg(seg))
{
auto seg_name = get_segm_name(seg);
msg(seg_name + "\n");
if (seg_name == ".text")
{
auto current_addr = seg;
auto end_addr = current_addr + 0x100000;
auto next_addr;
auto next_insn,next_op,next_op2;
while (current_addr != BADADDR && current_addr < end_addr)
{
auto insn_name = print_insn_mnem(current_addr);
auto op = print_operand(current_addr, 0);
auto op2 = print_operand(current_addr, 1);
if(insn_name == "xor" && op == "rax" && op2 == "rdx")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler1", 0);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "div" && op2 == "rbx")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
next_op = print_operand(next_addr, 0);
next_op2 = print_operand(next_addr, 1);
if(next_insn == "mov" && next_op == "rax" && next_op2 == "rdx")
{
SetHandlerToBpt(current_addr, "Handler2", 0);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "or" && op == "rax" && op2 == "rdx")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler3", 1);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "sub" && op == "rax" && op2 == "rdx")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler4", 0);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "shl" && op == "rax" && op2 == "cl")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler5", 1);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "and" && op == "rax" && op2 == "rdx")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler6", 0);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "shr" && op == "rax" && op2 == "cl")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler7", 1);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}

if(insn_name == "add" && op == "rax" && op2 == "rdx")
{
next_addr = next_head(current_addr, end_addr);
next_insn = print_insn_mnem(next_addr);
if(next_insn == "jmp")
{
SetHandlerToBpt(current_addr, "Handler9", 1);
SetHandlerToBpt(next_addr, "Handler8", 0);
}
}
current_addr = next_head(current_addr, end_addr);
}

}
}
}

trace 出的部分指令如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
0 / 7 = 0
12345679 - 0 = 12345679
0 / 4 = 0
32323232 << 0 = 32323232
32323232 >> 0 = 32323232
32323232 ^ F3F3ACB4 = C1C19E86
32323232 ^ B4B3522E = 8681601C
32323232 & 12345679 = 12301230
8681601C ^ 12301230 = 94B1722C
C1C19E86 ^ 94B1722C = 5570ECAA
31313131 - 5570ECAA = FFFFFFFFDBC04487
0 / 5 = 0
0 / 6 = 0
DBC04487 >> 0 = DBC04487
DBC04487 << 0 = DBC04487
DBC04487 ^ 23DDF732 = F81DB3B5
DBC04487 ^ 14C23B21 = CF027FA6
DBC04487 | 12345679 = DBF456FF
CF027FA6 ^ DBF456FF = 14F62959
F81DB3B5 ^ 14F62959 = ECEB9AEC
32323232 + ECEB9AEC = 11F1DCD1E
0 + 1 = 1
1 / 4 = 0
1 / 7 = 0
12345679 - 522E687E = FFFFFFFFC005EDFB
1F1DCD1E << 1 = 3E3B9A3C
1F1DCD1E >> 1 = F8EE68F
3E3B9A3C ^ F3F3ACB4 = CDC83688
F8EE68F ^ B4B3522E = BB3DB4A1
1F1DCD1E & C005EDFB = 5CD1A
BB3DB4A1 ^ 5CD1A = BB3879BB
CDC83688 ^ BB3879BB = 76F04F33
DBC04487 - 76F04F33 = 64CFF554
1 / 5 = 0
1 / 6 = 0
64CFF554 >> 1 = 3267FAAA
64CFF554 << 1 = C99FEAA8
3267FAAA ^ 23DDF732 = 11BA0D98
C99FEAA8 ^ 14C23B21 = DD5DD189
64CFF554 | C005EDFB = E4CFFDFF
DD5DD189 ^ E4CFFDFF = 39922C76
11BA0D98 ^ 39922C76 = 282821EE
1F1DCD1E + 282821EE = 4745EF0C
1 + 1 = 2
2 / 4 = 0
2 / 7 = 0
C005EDFB - 557B0378 = 6A8AEA83
4745EF0C << 2 = 11D17BC30
4745EF0C >> 2 = 11D17BC3
1D17BC30 ^ F3F3ACB4 = EEE41084
11D17BC3 ^ B4B3522E = A56229ED
4745EF0C & 6A8AEA83 = 4200EA00
A56229ED ^ 4200EA00 = E762C3ED
EEE41084 ^ E762C3ED = 986D369
64CFF554 - 986D369 = 5B4921EB
2 / 6 = 0
2 / 5 = 0
5B4921EB >> 2 = 16D2487A
5B4921EB << 2 = 16D2487AC
16D2487A ^ 23DDF732 = 350FBF48
6D2487AC ^ 14C23B21 = 79E6BC8D
5B4921EB | 6A8AEA83 = 7BCBEBEB
79E6BC8D ^ 7BCBEBEB = 22D5766
350FBF48 ^ 22D5766 = 3722E82E
4745EF0C + 3722E82E = 7E68D73A
2 + 1 = 3
3 / 4 = 0
3 / 7 = 0
6A8AEA83 - 8244D52C = FFFFFFFFE8461557

根据数据流同构出加密代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <windows.h>
#include <stdint.h>
int main()
{
uint32_t A,B;
uint32_t key[] = {0xF3F3ACB4,0xB4B3522E,0x23DDF732,0x14C23B21};
uint32_t input[] = {0x31313131,0x32323232,0x33333333,0x34343434,0x35353535,0x36363636,0x37373737,0x38383838,0x39393939,0x30303030,0x31313131,0x32323232};
uint32_t sum_sub[114] = {
0,0x522E687E, 0x557B0378, 0x8244D52C, 0x5C448F8C, 0x629E21B2, 0xB28619AA, 0x82F3746E, 0xAC1DDAC0,
0xA0477863, 0x3A5C138, 0xB18205B0, 0x58BA85EC, 0x405032EE, 0x9D9A9360, 0x44E4D3F9, 0xFEF76750,
0xD47E15C7, 0xCD00870E, 0xF5BF8EDE, 0x3DD89734, 0x811DE12D, 0xCFC2D132, 0xD1B4661A, 0xB99590E8,
0xF7C98504, 0x3ADCD4E8, 0xD53D1764, 0x784FE94, 0x1AA44FF5, 0xFB007960, 0x86BE4A1, 0x8F467020,
0x3D705192, 0x6C11A02C, 0xD9E012D7, 0x3CF36D58, 0xB0A3F503, 0x4EA925CE, 0xD4448A8, 0xC91E9F48,
0x9FC1F8C5, 0x17B238AC, 0xD908C9C2, 0x5A364984, 0x83E74DA, 0x1551C22C, 0x5648E3D9, 0x567F3B0,
0x6FC3E73B, 0x5F4D0634, 0xECDC1821, 0xF41DACA8, 0xA0275AB, 0xA5B01538, 0x6A96DDCB, 0x2C7E3C18,
0xBA8A813D, 0x470E6262, 0x2C15F05F, 0x975D4B08, 0x312ACB81, 0xBEB43A46, 0x77BC5005, 0xA3D90400,
0x1D1518AA, 0xBAC2D6E, 0xA8874213, 0xACD31BA4, 0xE5998224, 0x33E23C44, 0x8569CBB1, 0xB39090A8,
0x3E74DE2D, 0x1DB030F4, 0x5D73B653, 0xC86C7CC0, 0x7D302415, 0xD067862E, 0x150C238, 0x4B0AECD0,
0xA6D2992B, 0xD1526BE6, 0xC90AC785, 0xDA2AAA4C, 0x761487EE, 0xE1E0412E, 0xD7EA059B, 0x7A7D8870,
0xBD88AF69, 0x1291D1AA, 0xFCF88F42, 0x4DDDFD14, 0x55E22263, 0xC2D76AD2, 0xF226A96B, 0x21D18FC0,
0x4300CFBA, 0x81FCC5D2, 0x33778BCD, 0x21F5BF28, 0xAB0F8127, 0xB774AAA0, 0x3E161403, 0x4668BB30,
0x96142323, 0x24AC5204, 0xBC86C121, 0x9302378C, 0xA759B71A, 0x5AA1F3BE, 0x1B2456B7, 0xDBAB4C90,
0xBE6D41EF
};
for(int j = 0; j < 12; j += 2)
{
int sum = 0x12345679;
A = input[j];
B = input[j + 1];
for(int i = 0; i < 114; i++)
{
sum -= sum_sub[i];
A -= ((B << (i % 7)) ^ key[0]) ^ (((B >> (i % 4)) ^ key[1]) ^ (B & sum));
B += ((A >> (i % 5)) ^ key[2]) ^ (((A << (i % 6)) ^ key[3]) ^ (A | sum));
}
input[j] = A;
input[j + 1] = B;
}

for(int i = 0; i < 12; i++)
{
printf("0x%X,",input[i]);
}

}

解密代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <windows.h>
#include <stdint.h>
#define ROL32(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
int main()
{
uint32_t A,B;
uint32_t key[] = {0xF3F3ACB4,0xB4B3522E,0x23DDF732,0x14C23B21};
uint32_t data[] = {0xEC6185B2, 0xD47844B8, 0x00EB9EE7, 0x17C6A398, 0xF6CF87F7, 0x04AE0512, 0xA7F53F10, 0x6A1A83DB,
0x9F52BFEF, 0xC933183C, 0x9830FD51, 0xAB852172};
for(int i = 0; i < 12; i++)
{
data[i] = ROL32(data[i],16);
}
uint32_t sum_sub[114] = {
0,0x522E687E, 0x557B0378, 0x8244D52C, 0x5C448F8C, 0x629E21B2, 0xB28619AA, 0x82F3746E, 0xAC1DDAC0,
0xA0477863, 0x3A5C138, 0xB18205B0, 0x58BA85EC, 0x405032EE, 0x9D9A9360, 0x44E4D3F9, 0xFEF76750,
0xD47E15C7, 0xCD00870E, 0xF5BF8EDE, 0x3DD89734, 0x811DE12D, 0xCFC2D132, 0xD1B4661A, 0xB99590E8,
0xF7C98504, 0x3ADCD4E8, 0xD53D1764, 0x784FE94, 0x1AA44FF5, 0xFB007960, 0x86BE4A1, 0x8F467020,
0x3D705192, 0x6C11A02C, 0xD9E012D7, 0x3CF36D58, 0xB0A3F503, 0x4EA925CE, 0xD4448A8, 0xC91E9F48,
0x9FC1F8C5, 0x17B238AC, 0xD908C9C2, 0x5A364984, 0x83E74DA, 0x1551C22C, 0x5648E3D9, 0x567F3B0,
0x6FC3E73B, 0x5F4D0634, 0xECDC1821, 0xF41DACA8, 0xA0275AB, 0xA5B01538, 0x6A96DDCB, 0x2C7E3C18,
0xBA8A813D, 0x470E6262, 0x2C15F05F, 0x975D4B08, 0x312ACB81, 0xBEB43A46, 0x77BC5005, 0xA3D90400,
0x1D1518AA, 0xBAC2D6E, 0xA8874213, 0xACD31BA4, 0xE5998224, 0x33E23C44, 0x8569CBB1, 0xB39090A8,
0x3E74DE2D, 0x1DB030F4, 0x5D73B653, 0xC86C7CC0, 0x7D302415, 0xD067862E, 0x150C238, 0x4B0AECD0,
0xA6D2992B, 0xD1526BE6, 0xC90AC785, 0xDA2AAA4C, 0x761487EE, 0xE1E0412E, 0xD7EA059B, 0x7A7D8870,
0xBD88AF69, 0x1291D1AA, 0xFCF88F42, 0x4DDDFD14, 0x55E22263, 0xC2D76AD2, 0xF226A96B, 0x21D18FC0,
0x4300CFBA, 0x81FCC5D2, 0x33778BCD, 0x21F5BF28, 0xAB0F8127, 0xB774AAA0, 0x3E161403, 0x4668BB30,
0x96142323, 0x24AC5204, 0xBC86C121, 0x9302378C, 0xA759B71A, 0x5AA1F3BE, 0x1B2456B7, 0xDBAB4C90,
0xBE6D41EF
};
for(int j = 0; j < 12; j += 2)
{
int sum = 0x12345679;
for(int k = 0; k < 114; k++)
{
sum -= sum_sub[k];
}
A = data[j];
B = data[j + 1];
for(int i = 113; i >= 0; i--)
{
B -= ((A >> (i % 5)) ^ key[2]) ^ (((A << (i % 6)) ^ key[3]) ^ (A | sum));
A += ((B << (i % 7)) ^ key[0]) ^ (((B >> (i % 4)) ^ key[1]) ^ (B & sum));
sum += sum_sub[i];
}
data[j] = A;
data[j + 1] = B;
}

for(int i = 0; i < 12; i++)
{
for(int j = 0; j < 4; j++)
{
//printf("%c",(data[i] >> (j * 8))& 0xFF);
}
}

uint8_t flag[] = "gfalL{_Cu4Gn_43g_1tNsc41P50_t34rO0_r1vr31oD4}n?G";
for(int i = 0; i < 48; i += 4)
{
printf("%c%c%c%c",flag[i + 1],flag[i + 3],flag[i + 2],flag[i]);
}
// flag{C_L4nGu4g3_1Nt_c14s5_0P3r4t0r_Ov3r1o4D1nG?}
}

# Whimsical_Ideas_Happy_SShheeeepp

先看题目描述

1
2
3
4
5
Welcome to the Green Green Grassland.
Catch all the sheep hiding in the sheep village.
Start the game after running "Init.exe" as an administrator.
Use this directory as the working directory, and in the command line (such as cmd.exe or PowerShell), enter ".\catch_[key].txt" to catch the sheep.
Flag is "n1ctf{[happy sshheeeepp key][beauty sshheeeepp key][lazy sshheeeepp key][boil sshheeeepp key][warm sshheeeepp key][slow sshheeeepp key]}".

这边要求先运行 init.exe,逆向后发现 init 用来启动 IronGate.sys

逆向 IeonGate.sys 逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
__int64 __fastcall sub_140001000(PFLT_CALLBACK_DATA CallbackData)
{
PFLT_FILE_NAME_INFORMATION v2; // rcx
PFLT_IO_PARAMETER_BLOCK v3; // rbx
PCHAR v4; // rax
PFLT_FILE_NAME_INFORMATION v5; // rbx
PFILE_OBJECT TargetFileObject; // rdx
PFLT_IO_PARAMETER_BLOCK Iopb; // rbx
PCHAR IrpName; // rax
PFLT_FILE_NAME_INFORMATION FileNameInformation; // [rsp+38h] [rbp-D0h] BYREF
unsigned int DestinationString; // [rsp+40h] [rbp-C8h] BYREF
struct _UNICODE_STRING DestinationString_8; // [rsp+48h] [rbp-C0h] BYREF
WCHAR SourceString[512]; // [rsp+58h] [rbp-B0h] BYREF

if ( (dword_140003028 & 1) != 0 )
DbgPrint("IronGate!PreOperation: Entered\n");
if ( (int)sub_140001318(&FileNameInformation, CallbackData) < 0 )
return 0;
v2 = FileNameInformation;
if ( FileNameInformation->FinalComponent.Length != 28 )
{
LABEL_12:
if ( v2->FinalComponent.Length == 42 )
{
if ( RtlCompareMemory(Src, v2->FinalComponent.Buffer, 0x22u) == 34 && (dword_140003028 & 2) != 0 )// "SheepVillage.txt:"
{
Iopb = CallbackData->Iopb;
IrpName = FltGetIrpName(Iopb->MajorFunction);
DbgPrint(
"pre!ADSp: IrpMj=%02x.%02x %s Name=\"%ws\"\n",
Iopb->MajorFunction,
Iopb->MinorFunction,
IrpName,
FileNameInformation->Name.Buffer);
}
v2 = FileNameInformation;
}
FltReleaseFileNameInformation(v2);
return 0;
}
if ( RtlCompareMemory(Source1, FileNameInformation->FinalComponent.Buffer, 0xCu) != 12 )// "catch_"
{
v2 = FileNameInformation;
goto LABEL_12;
}
if ( (dword_140003028 & 2) != 0 )
{
v3 = CallbackData->Iopb;
v4 = FltGetIrpName(v3->MajorFunction);
DbgPrint(
"pre!ViEn: IrpMj=%02x.%02x %s Name=\"%ws\"\n",
v3->MajorFunction,
v3->MinorFunction,
v4,
FileNameInformation->Name.Buffer);
}
sub_140001980(SourceString, 0, 1024);
v5 = FileNameInformation;
memmove(SourceString, FileNameInformation->Name.Buffer, FileNameInformation->Name.Length - 28LL);
memmove((char *)&FileNameInformation + v5->Name.Length + 4, Src, (unsigned __int16)qword_140003018);
DestinationString = 0;
enc(FileNameInformation->FinalComponent.Buffer + 6, &DestinationString);
base64(
&DestinationString,
(char *)&SourceString[-14] + (unsigned __int16)qword_140003018 + FileNameInformation->Name.Length);
RtlCreateUnicodeString(&DestinationString_8, SourceString);
if ( (dword_140003028 & 2) != 0 )
DbgPrint("pre!VEAD: Name=\"%ws\"\n", DestinationString_8.Buffer);
FltReleaseFileNameInformation(FileNameInformation);
TargetFileObject = CallbackData->Iopb->TargetFileObject;
TargetFileObject->FileName = DestinationString_8;
CallbackData->IoStatus.Information = 0;
CallbackData->IoStatus.Status = 260;
TargetFileObject->RelatedFileObject = 0;
FltSetCallbackDataDirty(CallbackData);
return 4;
}

可以看到这边获取题目要求中的 key,进行 enc 加密后再 base64 加密,密文是压缩包中给出的(用 7z 解压可以看到,bandzip 不太行),对其进行解密再按顺序拼接即可

依旧 gpt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import base64

def mix10(v):
v &= 0xFFFFFF
for _ in range(10):
v = (( (v << 7) & 0xFFFFFF) ^ (v >> 17)) & 0xFFFFFF
return v

def brute_find_pre_for_m(m_target):
for pre in range(0, 1 << 24):
if mix10(pre) == m_target:
return pre
return None

def int24_to_bytes_le(x):
return bytes([(x) & 0xFF, (x >> 8) & 0xFF, (x >> 16) & 0xFF])

def recover_key_from_driver_out(out4):
b = base64.b64decode(out4 + "==")
if len(b) != 3:
raise ValueError("decoded length not 3 for {!r}".format(out4))
m = b[0] | (b[1] << 8) | (b[2] << 16)
pre = brute_find_pre_for_m(m)
if pre is None:
raise RuntimeError("no pre found for M=0x%06X" % m)
pre_bytes = int24_to_bytes_le(pre)
key4 = base64.b64encode(pre_bytes).decode('ascii')
return key4, pre, m


if __name__ == "__main__"
driver_outputs = ["576O", "5Zac", "5rK4", "5pqW", "5oWi", "5oeS"]
for out4 in driver_outputs:
print("Driver out:", out4)
key4, pre, m = recover_key_from_driver_out(out4)
print(" mixed (M) = 0x%06X pre = 0x%06X recovered_input_key = %s" % (m, pre, key4))
print()

#lltynvs6mh9KmsvimmtamheK