西湖论剑 2023 初赛 RE 复现

西湖论剑 2023 初赛 RE 复现

Dual personality

​ 第一次见到这种题目,好好做个记录。

​ 拿到题目文件,无壳 32 位 exe 文件。放入 ida 中分析,发现主函数没办法反编译。

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
; int __cdecl main(int argc, const char **argv, const char **envp)
.text:004013A0 _main: ; CODE XREF: invoke_main(void)+2E↓p
.text:004013A0 push ebp
.text:004013A1 mov ebp, esp
.text:004013A3 sub esp, 134h
.text:004013A9 push ebx
.text:004013AA push esi
.text:004013AB push edi
.text:004013AC lea edi, [ebp-74h]
.text:004013AF mov ecx, 1Dh
.text:004013B4 mov eax, 0CCCCCCCCh
.text:004013B9 rep stosd
.text:004013BB mov eax, ___security_cookie
.text:004013C0 xor eax, ebp
.text:004013C2 mov [ebp-4], eax
.text:004013C5 call sub_401000
.text:004013CA push offset unk_407060
.text:004013CF push offset a99s ; "%99s"
.text:004013D4 call sub_401620
.text:004013D9 add esp, 8
.text:004013DC push offset dword_4011D0
.text:004013E1 push 7
.text:004013E3 call heaven_gate
.text:004013E8 add esp, 8
.text:004013EB test eax, eax
.text:004013ED jz short loc_4013F4
.text:004013EF mov eax, dword_407058
.text:004013F4
.text:004013F4 loc_4013F4: ; CODE XREF: .text:004013ED↑j
.text:004013F4 sub eax, 21524111h
.text:004013F9 mov dword_407058, eax
.text:004013FE mov dword ptr [ebp-0Ch], offset unk_407060
.text:00401405 mov dword ptr [ebp-18h], 0
.text:0040140C jmp short loc_401417
.text:0040140E ; ---------------------------------------------------------------------------
.text:0040140E
.text:0040140E loc_40140E: ; CODE XREF: .text:0040144A↓j
.text:0040140E mov eax, [ebp-18h]
.text:00401411 add eax, 1
.text:00401414 mov [ebp-18h], eax
.text:00401417
.text:00401417 loc_401417: ; CODE XREF: .text:0040140C↑j
.text:00401417 cmp dword ptr [ebp-18h], 8
.text:0040141B jge short loc_40144C
.text:0040141D mov eax, [ebp-18h]
.text:00401420 mov ecx, [ebp-0Ch]
.text:00401423 mov edx, [ecx+eax*4]
.text:00401426 add edx, dword_407058
.text:0040142C mov eax, [ebp-18h]
.text:0040142F mov ecx, [ebp-0Ch]
.text:00401432 mov [ecx+eax*4], edx
.text:00401435 mov eax, [ebp-18h]
.text:00401438 mov ecx, [ebp-0Ch]
.text:0040143B mov edx, dword_407058
.text:00401441 xor edx, [ecx+eax*4]
.text:00401444 mov dword_407058, edx
.text:0040144A jmp short loc_40140E
.text:0040144C ; ---------------------------------------------------------------------------
.text:0040144C
.text:0040144C loc_40144C: ; CODE XREF: .text:0040141B↑j
.text:0040144C push 0
.text:0040144E lea eax, unk_407060
.text:00401454 push eax
.text:00401455 call fword ptr byte_40700C
.text:0040145B push offset dword_401290
.text:00401460 push 7
.text:00401462 call heaven_gate
.text:00401467 add esp, 8
.text:0040146A test eax, eax
.text:0040146C jnz short near ptr loc_40146F+4
.text:0040146E dec eax
.text:0040146F
.text:0040146F loc_40146F: ; CODE XREF: .text:0040146C↑j
.text:0040146F mov eax, dword_4070C8
.text:00401476 dec eax
.text:00401477 cmp eax, 20h ; ' '
.text:0040147A jz short loc_4014BC
.text:0040147C dec eax
.text:0040147D xor edx, edx
.text:0040147F dec eax
.text:00401480 mov ecx, 4
.text:00401480 ; ---------------------------------------------------------------------------
.text:00401485 db 3 dup(0)
.text:00401488 dd 0F1F74800h, 251C8D48h, 407014h, 4893148Ah, 0C825048Bh
.text:00401488 dd 48004070h, 60251C8Dh, 8A004070h, 0CA32030Ch, 48030C88h
.text:00401488 dd 8948C0FFh, 70C82504h, 0B2EB0040h
.text:004014BC ; ---------------------------------------------------------------------------
.text:004014BC
.text:004014BC loc_4014BC: ; CODE XREF: .text:0040147A↑j
.text:004014BC inc esp
.text:004014BD mov eax, offset dword_407000
.text:004014C2 dec eax
.text:004014C3 jmp fword ptr [eax]
.text:004014C5 ; ---------------------------------------------------------------------------
.text:004014C5 xor eax, eax
.text:004014C7 mov [ebp-44h], eax
.text:004014CA mov [ebp-40h], eax
.text:004014CD mov [ebp-3Ch], eax
.text:004014D0 mov [ebp-38h], eax
.text:004014D3 mov [ebp-34h], eax
.text:004014D6 mov [ebp-30h], eax
.text:004014D9 mov [ebp-2Ch], eax
.text:004014DC mov [ebp-28h], eax
.text:004014DF mov [ebp-24h], al
.text:004014E2 mov byte ptr [ebp-70h], 0AAh
.text:004014E6 mov byte ptr [ebp-6Fh], 4Fh ; 'O'
.text:004014EA mov byte ptr [ebp-6Eh], 0Fh
.text:004014EE mov byte ptr [ebp-6Dh], 0E2h
.text:004014F2 mov byte ptr [ebp-6Ch], 0E4h
.text:004014F6 mov byte ptr [ebp-6Bh], 41h ; 'A'
.text:004014FA mov byte ptr [ebp-6Ah], 99h
.text:004014FE
.text:004014FE loc_4014FE: ; CODE XREF: .text:004013E8↑J
.text:004014FE mov byte ptr [ebp-69h], 54h ; 'T'
.text:00401502 mov byte ptr [ebp-68h], 2Ch ; ','
.text:00401506 mov byte ptr [ebp-67h], 2Bh ; '+'
.text:0040150A mov byte ptr [ebp-66h], 84h
.text:0040150E mov byte ptr [ebp-65h], 7Eh ; '~'
.text:00401512 mov byte ptr [ebp-64h], 0BCh
.text:00401516 mov byte ptr [ebp-63h], 8Fh
.text:0040151A mov byte ptr [ebp-62h], 8Bh
.text:0040151E mov byte ptr [ebp-61h], 78h ; 'x'
.text:00401522 mov byte ptr [ebp-60h], 0D3h
.text:00401526 mov byte ptr [ebp-5Fh], 73h ; 's'
.text:0040152A mov byte ptr [ebp-5Eh], 88h
.text:0040152E mov byte ptr [ebp-5Dh], 5Eh ; '^'
.text:00401532 mov byte ptr [ebp-5Ch], 0AEh
.text:00401536 mov byte ptr [ebp-5Bh], 47h ; 'G'
.text:0040153A mov byte ptr [ebp-5Ah], 85h
.text:0040153E mov byte ptr [ebp-59h], 70h ; 'p'
.text:00401542 mov byte ptr [ebp-58h], 31h ; '1'
.text:00401546 mov byte ptr [ebp-57h], 0B3h
.text:0040154A mov byte ptr [ebp-56h], 9
.text:0040154E mov byte ptr [ebp-55h], 0CEh
.text:00401552 mov byte ptr [ebp-54h], 13h
.text:00401556 mov byte ptr [ebp-53h], 0F5h
.text:0040155A mov byte ptr [ebp-52h], 0Dh
.text:0040155E mov byte ptr [ebp-51h], 0CAh
.text:00401562 xor eax, eax
.text:00401564 mov [ebp-50h], al
.text:00401567 push 20h ; ' '
.text:00401569 lea eax, [ebp-70h]
.text:0040156C push eax
.text:0040156D push offset unk_407060
.text:00401572 call memcmp
.text:00401577 add esp, 0Ch
.text:0040157A test eax, eax
.text:0040157C jnz short loc_4015A6
.text:0040157E mov esi, esp
.text:00401580 push offset aRightFlagIsDas ; "Right, flag is DASCTF{your input}"
.text:00401585 call ds:puts
.text:0040158B add esp, 4
.text:0040158E cmp esi, esp
.text:00401590 call __RTC_CheckEsp
.text:00401595 mov esi, esp
.text:00401597 push 0
.text:00401599 call ds:__imp_exit
.text:0040159F ; ---------------------------------------------------------------------------
.text:0040159F cmp esi, esp
.text:004015A1 call __RTC_CheckEsp
.text:004015A6
.text:004015A6 loc_4015A6: ; CODE XREF: .text:0040157C↑j
.text:004015A6 mov esi, esp
.text:004015A8 push offset aWrongFlag ; "Wrong flag"
.text:004015AD call ds:puts
.text:004015B3 add esp, 4
.text:004015B6 cmp esi, esp
.text:004015B8 call __RTC_CheckEsp
.text:004015BD xor eax, eax
.text:004015BF push edx
.text:004015C0
.text:004015C0 loc_4015C0: ; CODE XREF: .text:00401467↑J
.text:004015C0 mov ecx, ebp
.text:004015C2 push eax
.text:004015C3 lea edx, dword_4015F0
.text:004015C9 call @_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:004015CE pop eax
.text:004015CF pop edx
.text:004015D0 pop edi
.text:004015D1 pop esi
.text:004015D2 pop ebx
.text:004015D3 mov ecx, [ebp-4]
.text:004015D6 xor ecx, ebp
.text:004015D8 call sub_4018A0
.text:004015DD add esp, 134h
.text:004015E3 cmp ebp, esp
.text:004015E5 call __RTC_CheckEsp
.text:004015EA mov esp, ebp
.text:004015EC pop ebp
.text:004015ED retn

​ 猜测其中存在花指令。我们对汇编进行逐步分析。

​ 第一个函数 sub_401000

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
int sub_401000()
{
int result; // eax
int i; // [esp+DCh] [ebp-54h]
int v2; // [esp+E8h] [ebp-48h]
int v3; // [esp+100h] [ebp-30h]
HMODULE ModuleHandleA; // [esp+118h] [ebp-18h]
DWORD flOldProtect[2]; // [esp+124h] [ebp-Ch] BYREF

__CheckForDebuggerJustMyCode(&unk_4080A2);
ModuleHandleA = GetModuleHandleA(0);
v3 = (int)ModuleHandleA + *((_DWORD *)ModuleHandleA + 15) + 4;
v2 = (int)ModuleHandleA
+ *((_DWORD *)ModuleHandleA + 15)
+ *(unsigned __int16 *)((char *)ModuleHandleA + *((_DWORD *)ModuleHandleA + 15) + 20)
+ 24;
for ( i = 0; ; ++i )
{
result = v3;
if ( i >= *(unsigned __int16 *)(v3 + 2) )
break;
VirtualProtect((char *)ModuleHandleA + *(_DWORD *)(v2 + 12), *(_DWORD *)(v2 + 20), 0x40u, flOldProtect);
v2 += 40;
}
return result;
}

​ 发现他对我们的输入并没有什么影响,主要是对进程做一些设置。

​ 下一个函数 sub_401620 传入的参数有 %99s,并且我们通过单步调试可以明确这是一个输入函数。

​ 再下面我们就来到了关键函数 heaven_gate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl heaven_gate(size_t Size, int a2)
{
char *v2; // ebx
char *retaddr; // [esp+D0h] [ebp+4h]

dword_407050 = VirtualAlloc(0, Size + 6, 0x3000u, 0x40u);
dword_407000 = (int)dword_407050;
memcpy(dword_407050, retaddr, Size);
v2 = (char *)dword_407050 + Size;
*v2 = -23;
*(_DWORD *)(v2 + 1) = &retaddr[Size] - v2 - 5;
v2[5] = 0xCC;
*retaddr = -22;
*(_DWORD *)(retaddr + 1) = a2;
*(_WORD *)(retaddr + 5) = 0x33; /* 段选择子 */
return 0;
}

​ 这里先记录一个知识点,x86-64 系统下的进程存在 32 位和 64 位两种工作模式,当处在 32 位工作模式下,段寄存器 CS 的值为 0x23,当初在 64 位工作模式下,段寄存器 CS 的值为 0x33,因为 cs 段寄存器存放的是段描述符在 GDT 的索引,CS为 0x33 在 GDT 表对应的段描述符为 64 位,CS 为 0x23 在 GDT 表对应的段描述符为 32 位。所以这里就存在一种方式在 32 位程序上执行 64 位代码的方法,被称作天堂之门。由于 32 位 ida 无法反编译 64 位的代码,所以这种技术极大的干扰了我们静态分析程序。

​ 通常,一般的 mov 指令没有办法直接修改 CS 段寄存器的值。所以这里就会使用到三条指令:

1
2
3
4
5
jmp far ptr

call far

retf

​ 首先 jmp far ptr 这个指令不仅会实现跳转,同时会修改 CS 寄存器的值。在 windbg 中对jmp far ptr 反汇编的汇编的代码例如 jmp 0x33:addr,冒号左边的值保存在 CS 寄存器中,而右边的值保存在 ip 中。

​ 同理 call far 也是这个道理,它的格式也如是 call 0x33:addr

​ retf 指令不同于普通的 ret 指令吗,他会从栈上弹出两个数据,一个赋值给 CS 寄存器,一个赋值给 ip。

​ 了解了这些,接下来我们来看这个函数。这个函数申请了一段内存,并且将这个函数接下来的几条指令复制一份放入申请的内存中。并同时修改两份代码。

1
2
3
# 对原本指令的修改
.text:004013E8 jmp far ptr loc_4014FE+2
.text:004013EF mov eax, dword_407058
1
2
3
4
5
6
7
8
9
# 对申请内存保存指令的修改
debug045:00420000 add esp, 8
debug045:00420003 test eax, eax
debug045:00420005 jz short loc_42000C
debug045:00420007 jmp loc_4013EF
debug045:0042000C ; ---------------------------------------------------------------------------
debug045:0042000C
debug045:0042000C loc_42000C: ; CODE XREF: debug045:00420005↑j
debug045:0042000C int 3 ; Trap to Debugger

​ 在指令修改后,我们可以看到 jmp far ptr,我们直接看其机器码就能发现熟悉的格式:

EA D0 11 40 00 33 00 -> jmp 0x33:0x4011D0

​ 我们可以得知跳转的目标地址处应该是一段 64 位代码,直接在 32 位 ida 上没办法进行分析。我们使用 010 editor 将这个 pe 文件的 pe 可选头的第一个 WORD 由 10B 改为 20B(32 位和 64 位的标识),这样就可以使用 64 位 ida 对 64 位代码进行反汇编,并且注意将分析程序重定位,保持与 32 位程序基地址一致(直接使用 windbg 会少不少事)。我们来看看目标地址处的 64 位代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
add_4011d0:
.text:00000000004011C4 align 10h
.text:00000000004011D0 mov rax, gs:60h ; 获取 PEB
.text:00000000004011D9 mov al, [rax+2] ; 获取 BeingDebugged 字段
.text:00000000004011DC mov byte_40705C, al
.text:00000000004011E3 test al, al ; 检测是否处于调试进程
.text:00000000004011E5 jnz short loc_4011F5 ; 调试状态下执行
.text:00000000004011E7 mov r12d, 5DF966AEh ; 非调试状态下执行
.text:00000000004011ED mov dword_407058, r12d
.text:00000000004011F5
.text:00000000004011F5 loc_4011F5: ; CODE XREF: .text:00000000004011E5↑j
.text:00000000004011F5 mov eax, 407000h ; 调试状态下执行
.text:00000000004011FB jmp qword ptr [rax]

​ 这里是一个反调试手段:BeingDebugged反调试技巧。gs:60h 获得 PEB 结构(进程环境块):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;
 mov al, [rax+2] 后。al的值就是 BeingDebugged 的值,调试状态为 1,非调试状态为 0。根据调试状态的不同执行不同的指令,这里的区别就是是否将 5DF966AE 赋值,参与后面的加密扩散的步骤。这里执行完毕后程序会跳转到申请的内存中,进而通过偏移跳转回程序的主函数继续执行。
1
2
3
4
5
6
7
8
9
# alloc 目标内存
debug045:00420000 add esp, 8
debug045:00420003 test eax, eax
debug045:00420005 jz short loc_42000C
debug045:00420007 jmp loc_4013EF
debug045:0042000C ; ---------------------------------------------------------------------------
debug045:0042000C
debug045:0042000C loc_42000C: ; CODE XREF: debug045:00420005↑j
debug045:0042000C int 3 ; Trap to Debugger

程序诸如此类的切换段选择子的方式还有两处:

第二处:

1
2
3
4
.text:0040144C push    0
.text:0040144E lea eax, input
.text:00401454 push eax
.text:00401455 call fword ptr byte_40700C

64位代码:

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
; __int64 __fastcall add_401200(__int64, __int64, __int64, __int64, __int64)
.text:0000000000401200 add_401200 proc far
.text:0000000000401200 push rbp
.text:0000000000401201 mov rbp, rsp
.text:0000000000401204 mov al, byte_40705C
.text:000000000040120D test al, al ; 再次检查是否处于调试状态
.text:000000000040120F jz short loc_401245 ; 非调试状态执行
.text:0000000000401211 mov rax, [rbp+10h]
.text:0000000000401215 mov rbx, [rax]
.text:0000000000401218 rol rbx, 20h
.text:000000000040121C mov [rax], rbx
.text:000000000040121F mov rbx, [rax+8]
.text:0000000000401223 rol rbx, 20h
.text:0000000000401227 mov [rax+8], rbx
.text:000000000040122B mov rbx, [rax+10h]
.text:000000000040122F rol rbx, 20h
.text:0000000000401233 mov [rax+10h], rbx
.text:0000000000401237 mov rbx, [rax+18h]
.text:000000000040123B rol rbx, 20h
.text:000000000040123F mov [rax+18h], rbx
.text:0000000000401243 jmp short loc_40127C
.text:0000000000401245 ; -------------------------------------------------------------------
.text:0000000000401245
.text:0000000000401245 loc_401245: ; CODE XREF: add_401200+F↑j
.text:0000000000401245 mov rax, [rbp+10h] ; 非调试状态执行
.text:0000000000401249 mov rbx, [rax] ; 获取待加密数据,8字节一组进行移位
.text:000000000040124C rol rbx, 0Ch ; 循环左移0xc
.text:0000000000401250 mov [rax], rbx ; 保存数据
.text:0000000000401253 mov rbx, [rax+8]
.text:0000000000401257 rol rbx, 22h
.text:000000000040125B mov [rax+8], rbx
.text:000000000040125F mov rbx, [rax+10h]
.text:0000000000401263 rol rbx, 38h
.text:0000000000401267 mov [rax+10h], rbx
.text:000000000040126B mov rbx, [rax+18h]
.text:000000000040126F rol rbx, 0Eh
.text:0000000000401273 mov [rax+18h], rbx
.text:0000000000401277 mov ebx, 0
.text:000000000040127C
.text:000000000040127C loc_40127C: ; CODE XREF: add_401200+43↑j
.text:000000000040127C mov ebx, 0
.text:0000000000401281 xor rax, rax
.text:0000000000401284 mov rsp, rbp
.text:0000000000401287 pop rbp
.text:0000000000401288 retf 8
.text:0000000000401288 add_401200 endp

第三处(同第一处由 haven_gate 函数申请内存保存并修改指令):

1
.text:00401467 jmp     far ptr loc_4015C0

64 位代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
add_401290:                             ; 第三次64位跳转
.text:0000000000401290 xor rax, rax
.text:0000000000401293 mov rax, 4014C5h
.text:000000000040129D mov dword_407000, eax
.text:00000000004012A4 lea rax, ds:407014h ; 初始key
.text:00000000004012AC mov bl, [rax]
.text:00000000004012AE mov cl, [rax+4]
.text:00000000004012B1 and bl, cl
.text:00000000004012B3 mov [rax], bl
.text:00000000004012B5 mov bl, [rax+4]
.text:00000000004012B8 mov cl, [rax+8]
.text:00000000004012BB or bl, cl
.text:00000000004012BD mov [rax+4], bl
.text:00000000004012C0 mov bl, [rax+8]
.text:00000000004012C3 mov cl, [rax+0Ch]
.text:00000000004012C6 xor bl, cl
.text:00000000004012C8 mov [rax+8], bl
.text:00000000004012CB mov bl, [rax+0Ch]
.text:00000000004012CE not bl
.text:00000000004012D0 mov [rax+0Ch], bl
.text:00000000004012D3 xor rax, rax
.text:00000000004012D6 jmp qword_407050

​ 这里对接下来参与加密异或的密钥进行了初始化。这部分执行完后,我发现程序仍然没法调试,说明仍处于 64 位的工作模式,再加上一开始审视主函数代码时发现,这里跳转回去后后几行出现无法被正常解析的数据,猜测这里仍处于 64 位模式工作,我们用 64 位 ida 静态分析其代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:000000000040146E                 mov     rax, qword_4070C8 ; 长度判断并对输入与密钥执行异或
.text:0000000000401476 cmp rax, 20h ; ' '
.text:000000000040147A jz short loc_4014BC
.text:000000000040147C xor rdx, rdx
.text:000000000040147F mov rcx, 4
.text:0000000000401489 div rcx
.text:000000000040148C lea rbx, ds:407014h
.text:0000000000401494 mov dl, [rbx+rdx*4]
.text:0000000000401497 mov rax, qword_4070C8
.text:000000000040149F lea rbx, ds:407060h
.text:00000000004014A7 mov cl, [rbx+rax]
.text:00000000004014AA xor cl, dl
.text:00000000004014AC mov [rbx+rax], cl
.text:00000000004014AF inc rax
.text:00000000004014B2 mov qword_4070C8, rax
.text:00000000004014BA jmp short loc_40146E ; 长度判断并对输入与密钥执行异或
.text:00000000004014BC ; -------------------------------------------------------------------
.text:00000000004014BC
.text:00000000004014BC loc_4014BC: ; CODE XREF: .text:000000000040147A↑j
.text:00000000004014BC mov eax, 407000h # 存储地址:4014C5
.text:00000000004014C2 jmp qword ptr [rax]

​ 由此我们分析出程序的加密逻辑:

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
dbg_get = 0x5DF966AE
input = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
dbg_get -= 0x21524111
list_input = []
qword_arr=[]
cmp_data = [0xE20F4FAA, 0x549941E4, 0x7E842B2C, 0x788B8FBC, 0x5E8873D3, 0x708547AE, 0xCE09B331, 0xCA0DF513]
origin_key = [0x9d, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x0, 0xb5, 0x0, 0x0, 0x0]
# 分组 & 扩散
for i in range(0, 32, 4):
list_input.append((ord(input[i + 3]) << 24) + (ord(input[i + 2]) << 16) + (ord(input[i + 1]) << 8) + ord(input[i]))
list_input[int(i/4)] = (list_input[int(i/4)] + dbg_get) & 0xffffffff
dbg_get ^= list_input[int(i/4)]
# print(hex(dbg_get))

# for i in range(8):
# print(hex(list_input[i]))

for i in range(4):
qword_arr.append((list_input[i*2+1] << 32) + list_input[i*2])

# for i in range(4):
# print(hex(qword_arr[i]))

qword_arr[0] = ((qword_arr[0] << 0xc) & 0xffffffffffffffff) | ((qword_arr[0] << 0xc) >> 64)
qword_arr[1] = ((qword_arr[1] << 0x22) & 0xffffffffffffffff) | ((qword_arr[1] << 0x22) >> 64)
qword_arr[2] = ((qword_arr[2] << 0x38) & 0xffffffffffffffff) | ((qword_arr[2] << 0x38) >> 64)
qword_arr[3] = ((qword_arr[3] << 0xe) & 0xffffffffffffffff) | ((qword_arr[3] << 0xe) >> 64)

# for i in range(4):
# print(hex(qword_arr[i]))

for i in range(4):
list_input[i*2+1] = qword_arr[i] >> 32
list_input[i*2] = qword_arr[i] & 0xffffffff

# for i in range(8):
# print(hex(list_input[i]))


# for i in range(4):
# print(hex(origin_key[i*4]) + ',',end='')
# print()
origin_key[0] = origin_key[0] & origin_key[4]
origin_key[4] = origin_key[4] | origin_key[8]
origin_key[8] = origin_key[8] ^ origin_key[0xc]
origin_key[0xc] = (-origin_key[0xc] - 1)& 0xff
# for i in range(4):
# print(hex(origin_key[i*4]) + ',',end='')

key = (origin_key[12] << 24) + (origin_key[8] << 16) + (origin_key[4] << 8) + origin_key[0]
print(hex(key))

for i in range(8):
list_input[i] ^= key

for i in range(8):
if list_input[i] != cmp_data[i]:
print('Wrong flag')
exit()
print('Right, flag is DASCTF{your input}')

由此我们写出解题脚本:

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
def xor_reverse(data, key):
for i in range(len(data)):
data[i] ^= key
return data

def rol_reverse(data, offset):
tmp_arr=[]
for i in range(4):
tmp_arr.append((data[i*2+1] << 32) + data[i*2])
for i in range(4):
tmp_arr[i] = ((tmp_arr[i] >> offset[i]) | ((tmp_arr[i] << (64-offset[i])) & 0xffffffffffffffff)) & 0xffffffffffffffff
for i in range(4):
data[2*i] = tmp_arr[i] & 0xffffffff
data[2*i+1] = tmp_arr[i] >> 32
return data

def deffusion_reverse(data, factor):
for i in range(len(data)):
tmp = data[i]
data[i] = (data[i] - factor) & 0xffffffff
factor ^= tmp
return data

def main():
cmp_data = [0xE20F4FAA, 0x549941E4, 0x7E842B2C, 0x788B8FBC, 0x5E8873D3, 0x708547AE, 0xCE09B331, 0xCA0DF513]
final_key = 0x4a827704
rol_offset = [0xc, 0x22, 0x38, 0xe]
diffusion_factor = 0x3CA7259D
flag_arr = xor_reverse(cmp_data, final_key)
flag_arr = rol_reverse(flag_arr, rol_offset)
flag_arr = deffusion_reverse(flag_arr, diffusion_factor)
print('DASCTF{', end='')
for i in flag_arr:
print(int.to_bytes(i, 4, 'little').decode(),end='')
print('}')

if __name__ == "__main__":
main()

flag:DASCTF{6cc1e44811647d38a15017e389b3f704}

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2022-2023 Syclover.Kama
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信