穿越火线ACE安全团队近期监测到一个代号为「影(Shadow)」的地下外挂组织
正在暗中壮大。经过数周的外围侦察,截获了「影」部署在入口节点上的一套门禁系统「宫殿」。只有通过该系统的验证,才能获取进入「影」内部网络的凭证。
「宫殿」的验证机制并不寻常:系统由用户态控制台和内核驱动组成,驱动内部隐藏着一个加密迷阵,所有操作指令必须由控制台通过驱动接口下发。表面上,系统不会对你的任何操作给出反馈——你无法直接判断每一步操作是成功推进,还是被防线拦截。
但「影」的工程师并不完美。ACE的研究员注意到,系统在运行时似乎并非完全沉默——某些异常现象暗示着隐藏的信息泄露。找到这些线索,你就能感知每一步的结果;发现得越多,破解效率越高。
你的任务:分析这套身份验证系统,发现所有隐藏的信息泄露,突破加密迷阵,
取出通关凭证。ACE 总部在等你的消息。

机制:

R3层应用负责创建隐藏的通信方式,并且与R0层通信,这里主要是打包操作码发出

R0层接收操作包后进行验证,核心验证代码被虚拟化保护了,前五次通信会依次采取五种反馈,五次反馈后或走了回头路,会开始随机反馈

前四种都找到了,但是始终没有找到最后一种,最后的脚本也是只用了四种接收方式

运行截图

R3层简析:

两个疑点,1.汇编层有很多字节都是被跳过的,2.SomeFlowerFunc里面结构不正常可以细看

这里发现两个事件对象创建,并且看名字是和迷宫有关的,应证了前面R0层 加密迷阵,有点像迷宫题?

再往后的两个函数看起来是SMC的作用,那么动调尝试去看

先得到两个信号量对象,后面有点像vmp的虚拟化混淆,这个驱动的通信方式是Io,所以尝试通过IoControl来直接定位

这里DeviceIoControl三种情况 0x8001200C 初始化 0x80012008 未知 0x80012004 位置控制码

然后多次动调分析,得到结论

先前的SomeFlowerFunc的作用1 创建对象, 作用2 创建信号量 但后面虚拟化的代码还没分析

函数尾部代码通过输入来决定push 哪段地址来决定接下来的分支去向

简单更改后可以看源码了

n76存输入,后面可以打印,dx作为操作码,传入通信

还原算法:(uint8_t)((((input ^ 0xFA) << 3) | (input ^ 0x40) >> 5))

v17,也就是高位记录次数大概结构就是

struct packet{ uint32_t opcode, uint32_t time, uint32_t time ^ opcode ^ 0xDEAD1337 };

那么R3层分析完毕

R0层分析

漏洞1、2 Event/Semaphore

简单去个花得到

然后可以搜索 DeviceIoControl(hDevice, 0x80012004``0x80012004 这个宏来查看,alt+b

走迷宫的应该在loc_40305A中,但是里面有类似VMP的虚拟化混淆,所以这里还是尝试通过别的方向入手

这里有先前创建的事件名称

这个函数通过n2决定打开哪个事件 0 2 OK 1 Wall,如此的话看n2参数来判断现在是否撞墙? - 或许可以写一个驱动hook掉这个函数,读参数2?

先双机调试下断点看一下

lmvm ShadowGateSys

看起来应该是可以的

那么这里就有一个想法了,驱动hook一下,然后写个自动化脚本去跑,额不对,那样子太麻烦了,直接看事件情况就好了

这里也有对信号量操作的,写了一个简单监听的程序,一开始还正常,得到地图

1
2
3
####
#S**
####

后面再次D之后,会收不到消息,这里windbg给隐藏通信函数下断点,发现断点是没有断住的,断点再往前试试

漏洞3 LastError

疑似又一种通信,这里5080不清晰,动调看一下

是 nt!PsGetThreadTeb:

那前文就是

给teb的0x68结构写值

那么GetLastError就可以获得这里的值了,不过这里是通信进程的GetLastError

结合前面1.事件2.信号量3.错误写了一个,发现还是有接收不到的,应该还有隐匿的,再次寻找

漏洞4 SetInfomationObject

动调发现

这里的函数调用时SetInfomationObject

前文

得到线程teb,那就是R3 teb的+1748处,后面SetInfomation设置的是00 01,是防止关闭,其实有猜测这里是不是根据可不可以关闭决定是否是撞墙

这里才发现,原来题目里面有提示5个通信方式啊,,,

那么我直接并排下断点

这样看就很明显了,第四个就是他,那么去详细的看一下参数,句柄相关信息

进入到SetInfomationObject函数中,进行参数拆解得到

ZwSetInformationObject(0xDC,4,0xfffffb88441ff490,2);

  • Handle - 一个用于放置信息的手柄。句柄不需要授予任何特定的访问权限。
  • ObjectInformationClass - 要设置的信息类型。
  • ObjectHandleFlagInformation (4)
    控制项处理继承和保护属性。
  • ObjectInformation - 指向包含请求特定数据的缓冲区的指针。
  • ObjectInformationLength - 提供的缓冲区大小(字节单位)。

这里改变的是这个句柄,但是刚好这个句柄是在teb+0x1748里面取出来的,那么也就是说,前文一直探测不到的原因有两个,1.跨进程还得去看teb的句柄,2.同进程我写的通信没有向0x1748中写句柄,那么调试看一下就确定了

通过不同的输入断点最后确定

撞墙 r8 - 00 00,走路 r8 - 00 01

漏洞5

实在没头绪乱翻交叉引用

这里5008,交叉引用

恰好在第五次操作断点断住,并且0FFFFF78000000320这个值有点眼熟,我记得是返回值的随机数用的

这里又有一个R3cookie,有点奇怪

查一下0FFFFF78000000320

https://connormcgarr.github.io/kuser-shared-data-changes-win-11/

这里可能分析有误,后面发现这里是第六次断下的,而且从这个时候开始日志就是随机产生的,所以我猜测这里是随机日志的代码,从第六次开始,漏洞随机

奇怪,之前发现发包的时候忘记带times了,但是如果带了我的第五次始终是漏洞4的成功。

无漏洞5打印地图

前面觉得的奇怪在后面灵验了,测试发现如下规律

  1. 5步之后,根据KUSER_SHARED_DATA结构.320h位进行随机,这里随机即会进行随机的漏洞输出,也会进行返回值的随机
  2. 每一步的随机应该是互不干预的,故如进入漏洞5,回退再次进入则可以避免漏洞5(钻了个空子)
  3. 如果一开始就DDA,那么直接进入随机漏洞,而不继续前五步的固定漏洞

ok,那么可以先编写一个脚本拿地图了

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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
#include <Windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <winternl.h>
#include <cstdint>
#include <stdint.h>
#include <intrin.h>
#include <string.h>
#include <conio.h>

#ifndef ObjectHandleFlagInformation
#define ObjectHandleFlagInformation ((OBJECT_INFORMATION_CLASS)4)
#endif

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif

typedef struct _OBJECT_HANDLE_FLAG_INFORMATION {
BOOLEAN Inherit;
BOOLEAN ProtectFromClose;
} OBJECT_HANDLE_FLAG_INFORMATION, * POBJECT_HANDLE_FLAG_INFORMATION;

typedef NTSTATUS(NTAPI* PFN_NtSetInformationObject)(
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength
);

typedef NTSTATUS(NTAPI* PFN_NtQueryObject)(
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength
);

namespace handle
{
HANDLE SemaphoreHandle_Wall = NULL;
HANDLE SemaphoreHandle_Ok = NULL;
HANDLE EventHandle_Ok = NULL;
HANDLE EventHandle_Wall = NULL;
HANDLE EventHandle_Hidden = NULL;
}

HANDLE hDevice;
HANDLE OKObject;
HANDLE WALLObject;
HANDLE newHandle;
HANDLE A7F3B2C1Object;
HANDLE B8E2C3D0Object;
ULONG* hideHandle;

bool iswall;

enum Type
{
Event = 1,
Semaphore = 2,
Error = 3,
Hide = 4,
};

uint8_t Type;
uint8_t isWall;
uint32_t times = 0;
char movlist[] = "WASD";

volatile LONG g_bindReady = 0;
volatile LONG g_bindFailed = 0;

#define OPEN_WAIT_MS 5000
#define BIND_READY_WAIT_MS 5000
#define UNKNOWN_RETRY_MAX 64

#define MAP_W 41
#define MAP_H 41
#define ORIGIN_X (MAP_W / 2)
#define ORIGIN_Y (MAP_H / 2)
#define CELL_UNKNOWN 0
#define CELL_ROAD 1
#define CELL_WALL 2
#define MAX_SEARCH_DEPTH 160

unsigned char mapData[MAP_H][MAP_W] = { 0 };
unsigned char visited[MAP_H][MAP_W] = { 0 };

DWORD WINAPI MyOpenSemaphore(LPVOID)
{
int twoflag;

printf("Wait Open Semaphore ... \n");
while (1)
{
twoflag = 0;

if (!handle::SemaphoreHandle_Ok)
{
handle::SemaphoreHandle_Ok = OpenSemaphoreW(
SYNCHRONIZE,
FALSE,
L"Global\\{A7F3B2C1-9E4D-4C8A-B5D6-1F2E3A4B5C6D}");
if (handle::SemaphoreHandle_Ok)
printf("Global\\{A7F3B2C1-9E4D-4C8A-B5D6-1F2E3A4B5C6D} Ready!\n");
}

if (!handle::SemaphoreHandle_Wall)
{
handle::SemaphoreHandle_Wall = OpenSemaphoreW(
SYNCHRONIZE,
FALSE,
L"Global\\{B8E2C3D0-0F5A-5D9B-C6E7-2A3F4B5C6D7E}");
if (handle::SemaphoreHandle_Wall)
printf("Global\\{B8E2C3D0-0F5A-5D9B-C6E7-2A3F4B5C6D7E} Ready!\n");
}

if (handle::SemaphoreHandle_Ok)
twoflag++;
if (handle::SemaphoreHandle_Wall)
twoflag++;

if (twoflag == 2)
break;

Sleep(1000);
}

printf("Semaphore All Ready!\n");
return 0;
}

DWORD WINAPI MyOpenHiddenEvent(LPVOID)
{
printf("Wait Open Hidden Event ... \n");

while (1)
{
if (!handle::EventHandle_Hidden)
{
handle::EventHandle_Hidden = OpenEventW(
SYNCHRONIZE | EVENT_MODIFY_STATE,
FALSE,
L"Global\\{E8D7C6B5-A4F3-2E1D-0C9B-8A7F6E5D4C3B}");

if (handle::EventHandle_Hidden)
{
printf("Hidden Event Ready! Global\\{E8D7C6B5-A4F3-2E1D-0C9B-8A7F6E5D4C3B}\n");
break;
}
}

Sleep(1000);
}

return 0;
}

DWORD WINAPI MyOpenEvent(LPVOID)
{
int twoflag;

printf("Wait Open Event ... \n");
while (1)
{
twoflag = 0;

if (!handle::EventHandle_Ok)
{
handle::EventHandle_Ok = OpenEventW(
SYNCHRONIZE | EVENT_MODIFY_STATE,
FALSE,
L"Global\\MazeMoveOK");
if (handle::EventHandle_Ok)
printf("MazeMoveOK Ready!\n");
}

if (!handle::EventHandle_Wall)
{
handle::EventHandle_Wall = OpenEventW(
SYNCHRONIZE | EVENT_MODIFY_STATE,
FALSE,
L"Global\\MazeMoveWall");
if (handle::EventHandle_Wall)
printf("MazeMoveWall Ready!\n");
}

if (handle::EventHandle_Ok)
twoflag++;
if (handle::EventHandle_Wall)
twoflag++;

if (twoflag == 2)
break;

Sleep(1000);
}

printf("Event All Ready!\n");
return 0;
}

void wall()
{
isWall = 1;
{
switch (Type)
{
case Event:
printf("[Event] ");
break;
case Semaphore:
printf("[Semaphore] ");
break;
case Error:
printf("[Error] ");
break;
case Hide:
printf("[Hide] ");
break;
}

printf("Is Wall\n");
}
}

void road()
{
isWall = 0;
{
switch (Type)
{
case Event:
printf("[Event] ");
break;
case Semaphore:
printf("[Semaphore] ");
break;
case Error:
printf("[Error] ");
break;
case Hide:
printf("[Hide] ");
break;
}
printf("Is Road\n");
}
}

DWORD WINAPI BindWithEvent(LPVOID lpParameter)
{
HANDLE* EventHandleList = (HANDLE*)lpParameter;
DWORD waitRet;

for (;;)
{
SetLastError(0);
waitRet = WaitForMultipleObjects(2, EventHandleList, FALSE, INFINITE);
Type = Type::Event;

if (waitRet == WAIT_OBJECT_0)
{
ResetEvent(handle::EventHandle_Ok);
road();
}
else if (waitRet == WAIT_OBJECT_0 + 1)
{
ResetEvent(handle::EventHandle_Wall);
wall();
}
else
{
printf("[ERROR] Bind Event failed: %lu\n", GetLastError());
break;
}
}

return 0;
}

DWORD WINAPI BindWithSemaphore(LPVOID lpParameter)
{
HANDLE* SemaphoreHandleList = (HANDLE*)lpParameter;
DWORD waitRet;

for (;;)
{
SetLastError(0);
waitRet = WaitForMultipleObjects(2, SemaphoreHandleList, FALSE, INFINITE);
Type = Type::Semaphore;

if (waitRet == WAIT_OBJECT_0)
{
road();
}
else if (waitRet == WAIT_OBJECT_0 + 1)
{
wall();
}
else
{
printf("[ERROR] Bind Semaphore failed: %lu\n", GetLastError());
break;
}
}

return 0;
}

void ErrInfo()
{
DWORD err = GetLastError();

switch (err)
{
case 0xC0DE0000:
Type = Type::Error;
wall();
break;
case 0xC0DE0001:
Type = Type::Error;
road();
break;
case 0xC0DE0002:
Type = Type::Error;
printf("[ERROR] Win\n");
break;
default:
break;
}
}

void HandleJudge()
{
HMODULE hNtdll;
PFN_NtQueryObject NtQueryObjectPtr;
HANDLE h;
NTSTATUS status;
ULONG retLen;
OBJECT_HANDLE_FLAG_INFORMATION flagInfo;

hNtdll = GetModuleHandleW(L"ntdll.dll");
if (!hNtdll)
return;

NtQueryObjectPtr = (PFN_NtQueryObject)GetProcAddress(hNtdll, "NtQueryObject");
if (!NtQueryObjectPtr)
return;

h = (HANDLE)(*hideHandle);
if (!h)
return;

ZeroMemory(&flagInfo, sizeof(flagInfo));
retLen = 0;
status = NtQueryObjectPtr(
h,
ObjectHandleFlagInformation,
&flagInfo,
sizeof(flagInfo),
&retLen
);

if (status < 0)
return;

if (Type == 0)
{
if (flagInfo.ProtectFromClose == 1 && flagInfo.Inherit == 0)
{
Type = Type::Hide;
road();
}
else if (flagInfo.ProtectFromClose == 0 && flagInfo.Inherit == 0)
{
Type = Type::Hide;
wall();
}
}
}

void SetHideHandleFlag()
{
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
if (!hNtdll)
return;

PFN_NtSetInformationObject NtSetInformationObjectPtr =
(PFN_NtSetInformationObject)GetProcAddress(hNtdll, "NtSetInformationObject");
if (!NtSetInformationObjectPtr)
return;

HANDLE h = (HANDLE)(*hideHandle);
if (!h)
return;

OBJECT_HANDLE_FLAG_INFORMATION flagInfo = {};
flagInfo.Inherit = TRUE;
flagInfo.ProtectFromClose = FALSE;

NTSTATUS status = NtSetInformationObjectPtr(
h,
ObjectHandleFlagInformation,
&flagInfo,
sizeof(flagInfo)
);

if (!NT_SUCCESS(status))
printf("[Hide] NtSetInformationObject failed: 0x%08X\n", status);
}

DWORD WINAPI bind(LPVOID)
{
HANDLE pThreadSemaphore = CreateThread(NULL, 0, MyOpenSemaphore, NULL, 0, 0);
HANDLE pThreadEvent = CreateThread(NULL, 0, MyOpenEvent, NULL, 0, 0);

WaitForSingleObject(pThreadSemaphore, INFINITE);
WaitForSingleObject(pThreadEvent, INFINITE);

CloseHandle(pThreadSemaphore);
CloseHandle(pThreadEvent);

HANDLE EventHandleList[2];
EventHandleList[0] = handle::EventHandle_Ok;
EventHandleList[1] = handle::EventHandle_Wall;

HANDLE SemaphoreHandleList[2];
SemaphoreHandleList[0] = handle::SemaphoreHandle_Ok;
SemaphoreHandleList[1] = handle::SemaphoreHandle_Wall;

printf("Bind!\n");
printf("============================\n");

HANDLE pThreadBindEvent = CreateThread(NULL, 0, BindWithEvent, EventHandleList, 0, 0);
HANDLE pThreadBindSemaphore = CreateThread(NULL, 0, BindWithSemaphore, SemaphoreHandleList, 0, 0);

InterlockedExchange(&g_bindReady, 1);
printf("[INFO] bind workers ready\n");

WaitForSingleObject(pThreadBindEvent, INFINITE);
WaitForSingleObject(pThreadBindSemaphore, INFINITE);

CloseHandle(pThreadBindEvent);
CloseHandle(pThreadBindSemaphore);

CloseHandle(handle::EventHandle_Ok);
CloseHandle(handle::EventHandle_Wall);
CloseHandle(handle::SemaphoreHandle_Ok);
CloseHandle(handle::SemaphoreHandle_Wall);

return 1;
}

BOOL FirstConnect(void* lpOutBuffer)
{
DWORD BytesReturned = 0;
return DeviceIoControl(hDevice, 0x8001200C, 0, 0, lpOutBuffer, 0x18u, &BytesReturned, 0);
}

BOOL ResetConnect(void* lpOutBuffer)
{
DWORD BytesReturned = 0;
return DeviceIoControl(hDevice, 0x80012008, 0, 0, lpOutBuffer, 0x18u, &BytesReturned, 0);
}

HANDLE GetR0Handle(void)
{
DWORD err;

hDevice = CreateFileW(
L"\\\\.\\ShadowGate",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);

if (hDevice == INVALID_HANDLE_VALUE)
{
err = GetLastError();

wprintf(L"[ERROR] Failed to open device '%ws'\n", L"\\\\.\\ShadowGate");
printf("[ERROR] Error code: %lu (0x%08lX)\n", err, err);

if (err == ERROR_FILE_NOT_FOUND)
{
printf("[HINT] Driver not loaded. Use: sc create ShadowGate type=kernel binPath= <path>\\ShadowGateSys.sys\n");
printf("[HINT] Then: sc start ShadowGate\n");
return NULL;
}

if (err == ERROR_ACCESS_DENIED)
printf("[HINT] Run as Administrator.\n");

return INVALID_HANDLE_VALUE;
}

return hDevice;
}

void MazeMoveEnvent()
{
OKObject = CreateEventW(0, 1, 0, L"Global\\MazeMoveOK");
WALLObject = CreateEventW(0, 1, 0, L"Global\\MazeMoveWall");
newHandle = CreateEventW(0, 1, 0, L"Global\\{E8D7C6B5-A4F3-2E1D-0C9B-8A7F6E5D4C3B}");

if (!OKObject || !WALLObject || !newHandle)
printf("[ERROR] CreateEventW failed: %lu\n", GetLastError());

PTEB pTEB = NtCurrentTeb();
hideHandle = (ULONG*)((uint8_t*)pTEB + 0x1748);
*hideHandle = (ULONG)newHandle;
}

void Mazeaa()
{
A7F3B2C1Object = CreateSemaphoreW(0, 0, 16, L"Global\\{A7F3B2C1-9E4D-4C8A-B5D6-1F2E3A4B5C6D}");
B8E2C3D0Object = CreateSemaphoreW(0, 0, 16, L"Global\\{B8E2C3D0-0F5A-5D9B-C6E7-2A3F4B5C6D7E}");
}

void CreateTwo()
{
MazeMoveEnvent();
Mazeaa();
}

struct packet
{
uint32_t opcode;
uint32_t time;
uint32_t m;
};

void init()
{
HANDLE hd = GetR0Handle();
(void)hd;
CreateTwo();
Sleep(100);

unsigned char buf[100] = { 0 };
if (!FirstConnect(buf))
{
printf("[ERROR] FirstConnect failed: %lu\n", GetLastError());
return;
}

for (int i = 0; i < 0x18u; i++)
printf("%02X ", buf[i]);
printf("\n");
}

char GetOpposite(char c)
{
if (c == 'W') return 'S';
if (c == 'S') return 'W';
if (c == 'A') return 'D';
if (c == 'D') return 'A';
return 0;
}

void StepPos(char move, int* x, int* y)
{
if (move == 'A')
(*x)--;
else if (move == 'D')
(*x)++;
else if (move == 'W')
(*y)--;
else if (move == 'S')
(*y)++;
}

uint8_t Strtoopcode(uint8_t move)
{
uint8_t ret = 0;
uint8_t opcode[4] = { 0x10, 0x20, 0x30, 0x40 };

switch (move)
{
case 'W':
ret = opcode[0];
break;
case 'S':
ret = opcode[1];
break;
case 'A':
ret = opcode[2];
break;
case 'D':
ret = opcode[3];
break;
default:
break;
}

return ret;
}

BOOL Mov(uint8_t mov)
{
Type = 0;
SetLastError(0); //error
SetHideHandleFlag(); //hide

struct packet inbuf;
inbuf.opcode = (uint32_t)((uint8_t)((((mov ^ 0xFA) << 3) | (mov ^ 0x40) >> 5)));
inbuf.time = times;
inbuf.m = inbuf.opcode ^ times ^ 0xDEAD1337;

unsigned char OutBuffer[0x84] = { 0 };
DWORD lpBytesReturned = 0;

return DeviceIoControl(
hDevice,
0x80012004,
&inbuf,
sizeof(inbuf),
OutBuffer,
sizeof(OutBuffer),
&lpBytesReturned,
NULL
);

times++;
}

void ConnectWithDriver(uint8_t move)
{
printf("mov -> %c\t", move);

do
{
Mov(Strtoopcode(move));

ErrInfo();

HandleJudge();

if (Type == 0)
{
Mov(Strtoopcode(GetOpposite(move)));
continue;
}
else
{
break;
}
} while (1);
}

int IsInside(int x, int y)
{
return x >= 0 && x < MAP_W && y >= 0 && y < MAP_H;
}

void InitMap()
{
ZeroMemory(mapData, sizeof(mapData));
ZeroMemory(visited, sizeof(visited));
mapData[ORIGIN_Y][ORIGIN_X] = CELL_ROAD;
}

void ExploreDFS(int x, int y, int depth)
{
if (depth >= MAX_SEARCH_DEPTH)
{
printf("[INFO] max depth reached at (%d, %d)\n", x - ORIGIN_X, y - ORIGIN_Y);
return;
}

visited[y][x] = 1;

for (int i = 0; i < 4; i++)
{
char mv = movlist[i];
int nx = x;
int ny = y;

StepPos(mv, &nx, &ny);
if (!IsInside(nx, ny))
continue;

if (mapData[ny][nx] != CELL_UNKNOWN)
continue;

ConnectWithDriver(mv);
if (isWall)
{
mapData[ny][nx] = CELL_WALL;
printf("wall: (%d, %d)\n", nx - ORIGIN_X, ny - ORIGIN_Y);
continue;
}

mapData[ny][nx] = CELL_ROAD;
printf("road: (%d, %d)\n", nx - ORIGIN_X, ny - ORIGIN_Y);

if (!visited[ny][nx])
ExploreDFS(nx, ny, depth + 1);

ConnectWithDriver(GetOpposite(mv));
if (isWall)
{
printf("[ERROR] backtrack blocked at (%d, %d) via %c\n",
nx - ORIGIN_X, ny - ORIGIN_Y, GetOpposite(mv));
ExitProcess(1);
}
}
}

void PrintMap()
{
int minX = MAP_W, minY = MAP_H, maxX = -1, maxY = -1;

for (int y = 0; y < MAP_H; y++)
{
for (int x = 0; x < MAP_W; x++)
{
if (mapData[y][x] != CELL_UNKNOWN)
{
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
}

if (maxX < 0 || maxY < 0)
{
printf("[ERROR] map is empty\n");
return;
}

printf("\n");
for (int y = minY; y <= maxY; y++)
{
for (int x = minX; x <= maxX; x++)
{
if (x == ORIGIN_X && y == ORIGIN_Y)
{
printf("S");
}
else if (mapData[y][x] == CELL_ROAD)
{
printf(".");
}
else if (mapData[y][x] == CELL_WALL)
{
printf("#");
}
else
{
printf("?");
}
}
printf("\n");
}
printf("\n");
}

int WaitBindReady()
{
ULONGLONG begin = GetTickCount64();

while (!InterlockedCompareExchange(&g_bindReady, 0, 0))
{
if (InterlockedCompareExchange(&g_bindFailed, 0, 0))
return 0;

if (GetTickCount64() - begin >= BIND_READY_WAIT_MS)
return 0;

Sleep(10);
}

return 1;
}

int main()
{
init();

if (!hDevice || hDevice == INVALID_HANDLE_VALUE)
return 1;

unsigned char buf[100] = { 0 };
if (!ResetConnect(buf))
{
printf("[ERROR] ResetConnect failed: %lu\n", GetLastError());
return 1;
}

HANDLE hBindThread = CreateThread(NULL, 0, bind, NULL, 0, 0);
if (!hBindThread)
{
printf("[ERROR] CreateThread(bind) failed: %lu\n", GetLastError());
return 1;
}

if (!WaitBindReady())
{
printf("[ERROR] bind startup failed or timed out\n");
return 1;
}

system("pause");

InitMap();
ExploreDFS(ORIGIN_X, ORIGIN_Y, 0);
PrintMap();

CloseHandle(hBindThread);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
###############
#S......#.....#
#######.###.#.#
#.....#.....#.#
#.###.#######.#
#.#.........#.#
#.#.#.#####.#.#
#.#.#.#...#.#.#
#.#.###.#.###.#
#.#.....#.#...#
#.#######.#.###
#...#...#.#.#.#
###.#.#.#.#.#.#
#.....#.#....E#
###############

最短路径脚本

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
from collections import deque

maze = [
"###############",
"#S......#.....#",
"#######.###.#.#",
"#.....#.....#.#",
"#.###.#######.#",
"#.#.........#.#",
"#.#.#.#####.#.#",
"#.#.#.#...#.#.#",
"#.#.###.#.###.#",
"#.#.....#.#...#",
"#.#######.#.###",
"#...#...#.#.#.#",
"###.#.#.#.#.#.#",
"#.....#.#....E#",
"###############"
]

rows = len(maze)
cols = len(maze[0])

start = None
end = None

for i in range(rows):
for j in range(cols):
if maze[i][j] == 'S':
start = (i, j)
elif maze[i][j] == 'E':
end = (i, j)

dirs = [
(-1, 0, 'U'),
(1, 0, 'D'),
(0, -1, 'L'),
(0, 1, 'R')
]

def bfs_shortest_path(maze, start, end):
queue = deque()
queue.append(start)

visited = set()
visited.add(start)

prev = {}
move_record = {}

while queue:
x, y = queue.popleft()

if (x, y) == end:
break

for dx, dy, op in dirs:
nx, ny = x + dx, y + dy

if 0 <= nx < rows and 0 <= ny < cols:
if maze[nx][ny] != '#' and (nx, ny) not in visited:
visited.add((nx, ny))
prev[(nx, ny)] = (x, y)
move_record[(nx, ny)] = op
queue.append((nx, ny))

if end not in prev and end != start:
return None, None

path_moves = []
path_points = []

cur = end
path_points.append(cur)

while cur != start:
path_moves.append(move_record[cur])
cur = prev[cur]
path_points.append(cur)

path_moves.reverse()
path_points.reverse()

return ''.join(path_moves), path_points

path_str, path_points = bfs_shortest_path(maze, start, end)

if path_str is None:
print("未找到从 S 到 E 的路径。")
else:
print("最短路径长度:", len(path_str))
print("最短路径方向:", path_str)
print("路径坐标:")
for p in path_points:
print(p)

maze_draw = [list(row) for row in maze]
for x, y in path_points:
if maze_draw[x][y] not in ('S', 'E'):
maze_draw[x][y] = '*'

print("\n标记最短路径后的迷宫:")
for row in maze_draw:
print(''.join(row))

1
2
3
4
5
6
7
8
=============================================
ACCESS GRANTED - Credential extracted!
=============================================
[CREDENTIAL] flag{SHAD0WNT_HYPERVMX}
[*] Your input (32 ops sent): RRRRRRDDRRRRUURRDDDDDDDDLLDDDDRR
[*] Note: blocked ops are included above; the driver only counts successful ones.

[*] Press any key to exit...

最终得到flag{SHAD0WNT_HYPERVMX}