NepCTF2025 XSafe WP
发表于|更新于
|浏览量:
Reverse XSafe
这题是这次比赛的零解驱动题,赛中看到是混淆就没有分析太多,赛后花了半天时间手动动调还是调出来了,难度就是在代码强混淆下动调分析数据(🐥神的混淆太狠了)。
R3
R3层程序代码很简单,就是输入字符串然后写入到自己进程的固定一个地址,这边就可以猜到是R0驱动通过一些手段直接访问该进程,获取到存在固定地址中的字符串进行校验。

R0
DriverEntry进来这边是原入口,可以发现被混淆了,并不能分析出来什么。


反调试处理
由于驱动存在反调试,第一步便是要去除反调试。
最快的方法就是直接加载驱动,然后让他触发反调试导致蓝屏,这时就可以通过调用栈找到反调试的地方。
加载驱动,蓝屏触发,

使用kb命令查看堆栈,发现是调用了KeBugCheckEx触发蓝屏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 1: kd> kb # RetAddr : Args to Child : Call Site 00 fffff803`4e118832 : ffffec83`b4fadee0 fffff803`4df7e210 00000000`00000d00 00000000`00000000 : nt!DbgBreakPointWithStatus 01 fffff803`4e117e16 : 00000000`00000003 ffffec83`b4fadee0 fffff803`4e015e50 00000000`00000d1e : nt!KiBugCheckDebugBreak+0x12 02 fffff803`4dffdf07 : ffffec83`b4fae430 ffffec83`b4fae470 ffffec83`b4fae5a0 ffffec83`b4fae460 : nt!KeBugCheck2+0x946 03 fffff803`4cab6f5c : 00000000`00000d1e 78696130`6a693233 00000000`00000033 00000000`00000000 : nt!KeBugCheckEx+0x107 04 fffff803`4cab4f22 : ffffec83`b4fae840 ffffec83`b4fae680 fffff803`4cab58ec fffff803`4cab4f18 : XSafe_____!malloc+0x5f5c 05 fffff803`4cc0c242 : 00000216`4229d9e0 00000216`4229d510 fffff803`4cc0b2ca 00000000`00000001 : XSafe_____!malloc+0x3f22 06 fffff803`4cb48b0e : 00000000`01000010 00000000`00040293 fffff803`4cb48a32 fffff803`4cb48aa9 : XSafe_____!malloc+0x15b242 07 fffff803`4cac6a64 : 00000000`00000000 fffff803`516bd536 fffff803`4cac69c7 fffff803`4cac6a19 : XSafe_____!malloc+0x97b0e 08 fffff803`4cbf4dcf : 00000000`00000000 00000000`00000000 ffffc78e`be4ff000 ffffc78e`bee5c9c0 : XSafe_____!malloc+0x15a64 09 fffff803`4cbf4e80 : ffffc78e`be4ff000 ffffec83`b4fb1a60 ffffc78e`be809290 ffffc78e`be765e50 : XSafe_____!malloc+0x143dcf 0a fffff803`4e35fc30 : ffffc78e`be4ff000 00000000`00000000 ffffc78e`be765e50 00000000`00000000 : XSafe_____!malloc+0x143e80 0b fffff803`4e36fd0d : 00000000`00000016 00000000`00000000 00000000`00000000 00000000`00001000 : nt!PnpCallDriverEntry+0x4c 0c fffff803`4e382a87 : 00000000`00000000 00000000`00000000 fffff803`4e925440 ffffc78e`bea2d6c0 : nt!IopLoadDriver+0x4e5 0d fffff803`4de22525 : ffffc78e`00000000 ffffffff`80002598 ffffc78e`be9e8040 00000000`00000000 : nt!IopLoadUnloadDriver+0x57 0e fffff803`4df29935 : ffffc78e`be9e8040 00000000`00000080 ffffc78e`b846a040 000fa47f`b19bbdff : nt!ExpWorkerThread+0x105 0f fffff803`4e006e08 : ffff8001`1f5a8180 ffffc78e`be9e8040 fffff803`4df298e0 00000000`00000000 : nt!PspSystemThreadStartup+0x55 10 00000000`00000000 : ffffec83`b4fb2000 ffffec83`b4fac000 00000000`00000000 00000000`00000000 : nt!KiStartSystemThread+0x28
|
03行的返回地址fffff803`4cab6f5c
就是驱动中调用KeBugCheckEx的地方。
下图,返回地址的上一行call,即为KeBugCheckEx的调用Call,IDA加载驱动,搜索这边汇编对应的硬编码字节定位到Call,然后Nop即可过这种反调试。
1 2
| fffff803`4cab6f56 ff158cfd1300 call qword ptr [0FFFFF8034CBF6CE8h] fffff803`4cab6f5c 4989cb mov r11, rcx
|

过了这个反调试,发现调试器被剥离了,大概是调用了KdDisableDebugger禁用了内核调试器。
我们发现调用KeBugCheckEx的call qword ptr [0FFFFF8034CBF6CE8h]
在许多api调用的时候都用到。
获取到0FFFFF8034CBF6CE8h地址的值跳转过去,可以发现是jmp rax,说明这个就是全局所有调用API都共用的代码,只需要断点这边,查看rax地址就能知道调用了什么API。

输入”sxe ld XSafe”,设置断点在驱动入口,加载驱动。
然后输入”.reload”重新加载符号,再输入”bp nt!KeBugCheckEx”断在KeBugCheckEx。
运行断住,找到ret的地方,直接输入”rrip=ret地址”,跳到ret处,就可以绕过执行。

单步ret回去,得到call的地址,然后断点在这个jmp rax上。

运行发现调用了KdDisableDebugger,同上面一样修改rip到ret跳过执行,执行到返回就可以找到调用方。


发现还会调用两次KeIpiGenericCall,同上,跳过执行,然后执行返回找到调用方。



综上,可以得到以下四处反调试调用方,IDA加载驱动,搜索每处地方的硬编码字节,全部把call qword ptr [0FFFFF803E5476CE8h]
这一行nop了,即可过全部反调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| call KeBugCheckEx: fffff803`4cab6f56 ff158cfd1300 call qword ptr [0FFFFF8034CBF6CE8h] fffff803`4cab6f5c 4989cb mov r11, rcx
call KdDisableDebugger: fffff803`e53350fc ff15e61b1400 call qword ptr [0FFFFF803E5476CE8h] fffff803`e5335102 e803000000 call FFFFF803E533510A
call KeIpiGenericCall: fffff803`e5336706 ff15dc051400 call qword ptr [0FFFFF803E5476CE8h] fffff803`e533670c 48b9e2ba9a1b451d75f0 mov rcx, 0F0751D451B9ABAE2h
call KeIpiGenericCall: fffff803`e53367ff ff15e3041400 call qword ptr [0FFFFF803E5476CE8h] fffff803`e5336805 4883c420 add rsp, 20h
|
流程分析
主流程
通过上面断点那个通用的jmp rax,然后运行查看rax地址,我们可以得到一条API调用链,可以看出是注册了回调,然后在回调里面获取当前进程名,然后判断。
1 2 3 4 5 6 7 8 9 10 11 12 13
| RtlInitUnicodeString
// 注册回调 ObRegisterCallbacks
// 获取当前进程 PsGetCurrentProcess
// 获取当前进程名 PsGetProcessImageFileName
// 字符串判断 strncmp
|
调用到strncmp的时候看一下参数,可以看到一个是PsGetProcessImageFileName获取到的当前进程名,一个是目标进程名,那么应该就是判断当前回调的进程是不是xia0ji233.exe,也就那个R3程序。

由于只要没有这个进程,之后的代码就不会执行,只能找到strncmp调用处,然后在他的下一行下一个条件断点,bp fffff803`e5341304 ".if(@eax!=0){gc}"
,让他在strncmp返回值eax==0的时候断下,然后运行xia0ji233.exe
,随便输入1111222233334444
,然后调试器会断下。

然后接下来又会有新的API通过jmp rax执行。
1 2 3 4 5 6 7 8 9 10 11 12 13
| // 获取进程段基地址 PsGetProcessSectionBaseAddress
ExAllocatePoolWithTag
// 获取当前进程 PsGetCurrentProcess
// 复制虚拟内存 MmCopyVirtualMemory
// 字符串长度 strlen
|
断在MmCopyVirtualMemory的时候查看一下寄存器,发现rdx参数是r3内存,da命令查询发现就是我们程序种输入的字符串。

Step Out执行到返回,然后rdi就是从r3复制字符串到r0的储存地址。

strlen传入了输入的字符串,获取长度,大概是要判断长度是否符合。
断到strlen的时候Steg Out执行到返回,然后开始单步调试(F8),
执行到此处xor时,此时r8d就是我们输入字符串长度,eax是通过计算得到的一个值,eax为0xCDB92702
,需要等于目标0xCDB92732
,说明目标长度就是0xCDB92732^0xCDB92702
,也就是48,所以flag输入长度为48.
1 2
| fffff803`e533d86d 4431c0 xor eax, r8d fffff803`e533d870 3d3227b9cd cmp eax, 0CDB92732h
|
此时步过执行xor,然后将eax改成目标值,让他通过条件,开始之后的加密相关代码执行,因为我们只需要分析他的加密流程,输入长度多少无所谓。
加密流程
分析加密,这边从输入的数据入手,ba r8 ffffd08f48d02a50
设置8字节硬件断点在输入的字符串地址,运行,会断在下面代码。
获取了第一个四字节到r11d和堆栈中,然后之后就是漫长的单步调试,观察主要的寄存器的变化,以及相关汇编代码。
1 2 3 4
| fffff803`e533bf76 448b1a mov r11d, dword ptr [rdx] fffff803`e533bf79 4889542410 mov qword ptr [rsp+10h], rdx fffff803`e533bf7e 7408 je FFFFF803E533BF88 fffff803`e533bf80 7506 jne FFFFF803E533BF88
|
初步获取前8个字节。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 获取第一个四字节到r11d fffff804`3964bf76 448b1a mov r11d, dword ptr [rdx] fffff804`3964bf79 4889542410 mov qword ptr [rsp+10h], rdx fffff804`3964bf7e 7408 je FFFFF8043964BF88 fffff804`3964bf80 7506 jne FFFFF8043964BF88 fffff804`3964bf82 9c pushfq
获取第二个四字节到r15d fffff804`3964bf88 c5f82ed0 vucomiss xmm2, xmm0 fffff804`3964bf8c 448b7a04 mov r15d, dword ptr [rdx+4] fffff804`3964bf90 0f2de5 cvtps2pi mm4, xmm5
|
中途会得到delta在寄存器中,0x9e3779b9
以下是对两个四字节相关的运算,可以很简单的看出是一个Tea加密,并且密钥在动调过程中就可以拿到{0x3c4ed885 ,0x12af3e87 ,0xd6e1b31f ,0x25c10aa0}
。
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
| r11d->rdx = 0x33333333 r15->r12 = 0x34343434 r15->rcx = 0x34343434 ecx << 4 (0x34343434<<4 = 0x0000000043434340)
fffff804`3964c2b3 4589fc mov r12d, r15d fffff804`3964c2b6 4489da mov edx, r11d fffff804`3964c2b9 4489f9 mov ecx, r15d fffff804`3964c2bc c1e104 shl ecx, 4 ===================
r15 >> 5 (0x34343434>>5 = 0x0000000001a1a1a1)
fffff804`3964c2d1 41c1ef05 shr r15d, 5 fffff804`3964c2d5 7405 je FFFFF8043964C2DC ===================
r15d = r15d ^ ecx = 0x0000000001a1a1a1^0x0000000043434340 = 0x0000000042e2e2e1 r15d = r15d + r12d = 0x0000000042e2e2e1+0x34343434 = 0x0000000077171715 esi->ecx(sum) = 0x0 ecx = ecx&3 = 0 [r8+rcx*4] -> r11d = 0x000000003c4ed885 r8为key 1: kd> dd r8 ffff8184`efd2a090 3c4ed885 12af3e87 d6e1b31f 25c10aa0 r11d = r11d + esi = (key[0]+sum) = 0x000000003c4ed885
fffff804`3964c2dc 4131cf xor r15d, ecx fffff804`3964c2df 4501e7 add r15d, r12d fffff804`3964c2e2 89f1 mov ecx, esi fffff804`3964c2e4 83e103 and ecx, 3 fffff804`3964c2e7 458b1c88 mov r11d, dword ptr [r8+rcx*4] fffff804`3964c2eb 4101f3 add r11d, esi ===================
r15d->ecx=0x0000000077171715 ecx = ecx ^ 9F377865h = 0x00000000e8206f70 r15d = r15d ^ 60C8879Ah = 0x0000000017df908f fffff804`3964c306 4489f9 mov ecx, r15d fffff804`3964c309 81f16578379f xor ecx, 9F377865h fffff804`3964c30f 4181f79a87c860 xor r15d, 60C8879Ah ===================
r11d -> r13d = 0x000000003c4ed885 r13d = r13d ^ 981F18CDh = 0x00000000a451c048 fffff804`3964c32a 4589dd mov r13d, r11d fffff804`3964c32d 4181f5cd181f98 xor r13d, 981F18CDh fffff804`3964c334 4151 push r9 ===================
r13d = r13d & r15d = 0x00000000a451c048 & 0x0000000017df908f = 0x0000000004518008 r11d = r11d ^ 67E0E732h = 0x000000003c4ed885 ^ 67E0E732h = 0x000000005bae3fb7 r11d = r11d & ecx = 0x000000005bae3fb7 & 0x00000000e8206f70 = 0x0000000048202f30 r11d = r11d ^ r13d = 0x0000000048202f30 ^ 0x0000000004518008 = 0x000000004c71af38 r11d = r11d ^ 72860A8h = 0x000000004c71af38 ^ 72860A8h = 0x000000004b59cf90 【上面计算可以简化总结为 r11d = r11d ^ r15d = 0x000000003c4ed885 ^ 0x0000000077171715 = 0x000000004b59cf90】 r11d = r11d + edx = 0x000000004b59cf90 + 0x0000000033333333 = 0x000000007e8d02c3 rsi = rsi + r10d = 0 + 0x000000009e3779b9 = sum + delta = 0x000000009e3779b9 r11d -> ecx = 0x000000007e8d02c3
fffff804`3964c348 4521fd and r13d, r15d fffff804`3964c34b 4181f332e7e067 xor r11d, 67E0E732h fffff804`3964c352 4121cb and r11d, ecx fffff804`3964c355 c4431540d7ee vdpps ymm10, ymm13, ymm15, 0EEh fffff804`3964c35b 4531eb xor r11d, r13d fffff804`3964c35e c44165d8d7 vpsubusb ymm10, ymm3, ymm15 fffff804`3964c363 4181f3a8602807 xor r11d, 72860A8h fffff804`3964c36a 4101d3 add r11d, edx fffff804`3964c36d c4c17e70ce22 vpshufhw ymm1, ymm14, 22h fffff804`3964c373 4401d6 add esi, r10d fffff804`3964c376 c5fd70c703 vpshufd ymm0, ymm7, 3 fffff804`3964c37b 4489d9 mov ecx, r11d fffff804`3964c37e e802000000 call FFFFF8043964C385 ===================
ecx = ecx << 4 = 0x000000007e8d02c3 << 4 = 0x00000000e8d02c30 r11d -> edx = 0x000000007e8d02c3 edx = edx >> 5 = 0x000000007e8d02c3 >> 5 = 0x0000000003f46816
fffff804`3964c395 c1e104 shl ecx, 4 fffff804`3964c398 c57e7ffe vmovdqu ymm6, ymm15 fffff804`3964c39c 4489da mov edx, r11d fffff804`3964c39f c4627d1cda vpabsb ymm11, ymm2 fffff804`3964c3a4 c1ea05 shr edx, 5 fffff804`3964c3a7 50 push rax fffff804`3964c3a8 e801000000 call FFFFF8043964C3AE ===================
edx = edx ^ ecx = 0x0000000003f46816 ^ 0x00000000e8d02c30 = 0x00000000eb244426 edx = edx + r11d = 0x00000000eb244426 + 0x000000007e8d02c3 = 0x0000000069b146e9 esi -> ecx = 0x000000009e3779b9(sum) ecx = ecx >> 9 = 0x000000009e3779b9 >> 9 = 0x00000000004f1bbc ecx = ecx & 0xC = 0x00000000004f1bbc & 0xC = 0x000000000000000c
fffff804`3964c3c4 31ca xor edx, ecx fffff804`3964c3c6 4401da add edx, r11d fffff804`3964c3c9 89f1 mov ecx, esi fffff804`3964c3cb 0f55e1 andnps xmm4, xmm1 fffff804`3964c3ce c1e909 shr ecx, 9 fffff804`3964c3d1 c57d28d4 vmovapd ymm10, ymm4 fffff804`3964c3d5 83e10c and ecx, 0Ch fffff804`3964c3d8 c4434540d552 vdpps ymm10, ymm7, ymm13, 52h fffff804`3964c3de e91a000000 jmp FFFFF8043964C3FD ===================
[r8+rcx] -> r15d = 0x0000000025c10aa0 r15d = r15d + esi(sum) = 0x0000000025c10aa0 + 0x000000009e3779b9 = 0x00000000c3f88459
fffff804`3964c402 458b3c08 mov r15d, dword ptr [r8+rcx] fffff804`3964c406 c442053dcb vpmaxsd ymm9, ymm15, ymm11 fffff804`3964c40b 4101f7 add r15d, esi fffff804`3964c40e c4c17e70ea21 vpshufhw ymm5, ymm10, 21h fffff804`3964c414 e805000000 call FFFFF8043964C41E ===================
edx -> ecx = 0x0000000069b146e9 ecx = ecx ^ 0B49FE5F4h = 0x0000000069b146e9 ^ 0B49FE5F4h = 0x00000000dd2ea31d edx = edx ^ 4B601A0Bh = 0x0000000069b146e9 ^ 4B601A0Bh = 0x0000000022d15ce2 r15d -> r13d = 0x00000000c3f88459 r13d = r13d ^ 0A8760961h = 0x000000006b8e8d38 r13d = r13d & edx = 0x000000006b8e8d38 & 0x0000000022d15ce2 = 0x0000000022800c20 r15d = r15d ^ 5789F69Eh = 0x00000000c3f88459 ^ 5789F69Eh = 0x00000000947172c7
fffff804`3964c431 89d1 mov ecx, edx fffff804`3964c433 81f1f4e59fb4 xor ecx, 0B49FE5F4h fffff804`3964c439 c4427d1def vpabsw ymm13, ymm15 fffff804`3964c43e 81f20b1a604b xor edx, 4B601A0Bh fffff804`3964c444 d8ec fsubr st, st(4) fffff804`3964c446 4589fd mov r13d, r15d fffff804`3964c449 d8cd fmul st, st(5) fffff804`3964c44b 4181f5610976a8 xor r13d, 0A8760961h fffff804`3964c452 4121d5 and r13d, edx fffff804`3964c455 4181f79ef68957 xor r15d, 5789F69Eh fffff804`3964c45c e801000000 call FFFFF8043964C462 ===================
r15d = r15d & ecx = 0x00000000947172c7 & 0x00000000dd2ea31d = 0x0000000094202205
fffff804`3964c475 4121cf and r15d, ecx fffff804`3964c478 e801000000 call FFFFF8043964C47E ===================
r15d = r15d ^ r13d = 0x0000000094202205 ^ 0x0000000022800c20 = 0x00000000b6a02e25 r15d = r15d ^ 1CE9EC95h = 0x00000000b6a02e25 ^ 1CE9EC95h = 0x00000000aa49c2b0 【上面计算可以简化总结为r15d = r15d ^ edx = 0x00000000c3f88459 ^ 0x0000000069b146e9 = 0x00000000aa49c2b0】 r15d = r15d + r12d = 0x00000000aa49c2b0 + 0x34343434 = 0x00000000de7df6e4
fffff804`3964c493 4531ef xor r15d, r13d fffff804`3964c496 4181f795ece91c xor r15d, 1CE9EC95h fffff804`3964c49d 4501e7 add r15d, r12d fffff804`3964c4a0 48b9b07050452eff9123 mov rcx, 2391FF2E455070B0h fffff804`3964c4aa 48b9abb442f15bbb4da8 mov rcx, 0A84DBB5BF142B4ABh fffff804`3964c4b4 4831c9 xor rcx, rcx fffff804`3964c4b7 75db jne FFFFF8043964C494 fffff804`3964c4b9 eb0d jmp FFFFF8043964C4C8 ===================
|
执行完上面所有计算代码,再直接运行,会再次因为原字符串被访问而断住,这两边就是将加密结果储存到原字符串内存中。
1 2 3 4 5 6 7 8 9 10 11 12
| 计算完的第一个四字节存到原字符串指针 0x9a11c776 fffff804`3964d434 58 pop rax fffff804`3964d435 58 pop rax fffff804`3964d436 448918 mov dword ptr [rax], r11d fffff804`3964d439 e800000000 call FFFFF8043964D43E ===================
计算完的第二个四字节存到原字符串指针 0xcd465e46 fffff804`3964d463 44897804 mov dword ptr [rax+4], r15d fffff804`3964d467 740b je FFFFF8043964D474 fffff804`3964d469 7509 jne FFFFF8043964D474 ===================
|
将以上计算过程转为C代码如下:
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
| void encrypt(DWORD *data) { DWORD Key[]{0x3c4ed885, 0x12af3e87, 0xd6e1b31f, 0x25c10aa0};
DWORD Delta = 0x9e3779b9;
DWORD d1{}, d2{};
d1 = data[0]; d2 = data[1];
DWORD Sum{};
for (int i = 0; i < 0xff; i++) { DWORD tmp1{}, tmp2{}, tmp3{};
tmp1 = ((d2 << 4) ^ (d2 >> 5)) + d2; tmp2 = Key[Sum & 3] + Sum; tmp3 = tmp1 ^ tmp2; d1 += tmp3;
Sum += Delta;
tmp1 = ((d1 << 4) ^ (d1 >> 5)) + d1; tmp2 = Key[((Sum >> 9) & 0xC) / 4] + Sum; tmp3 = tmp1 ^ tmp2; d2 += tmp3; }
data[0] = d1; data[1] = d2; }
|
对应解密代码:
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
| void decrypt(DWORD *data) { DWORD Key[]{0x3c4ed885, 0x12af3e87, 0xd6e1b31f, 0x25c10aa0};
DWORD Delta = 0x9e3779b9;
DWORD d1{}, d2{};
d1 = data[0]; d2 = data[1];
DWORD Sum = Delta * 0xff;
for (int i = 0; i < 0xff; i++) { DWORD tmp1{}, tmp2{}, tmp3{};
tmp1 = ((d1 << 4) ^ (d1 >> 5)) + d1; tmp2 = Key[((Sum >> 9) & 0xC) / 4] + Sum; tmp3 = tmp1 ^ tmp2; d2 -= tmp3;
Sum -= Delta;
tmp1 = ((d2 << 4) ^ (d2 >> 5)) + d2; tmp2 = Key[Sum & 3] + Sum; tmp3 = tmp1 ^ tmp2; d1 -= tmp3; }
data[0] = d1; data[1] = d2; }
|
接下来只需要密文即可,此时多次按g继续执行,然后会再次断在jmp rax处,然后过来会发现这边调用了一个api(下图)。
然后单步执行到箭头处下面部分,下面会进行多次的qword 8字节比对。

此时rcx就是输入的十六字节加密后的数据,rcx+rdx地方是密文,上文分析可知密文是48字节,提取rcx+rdx处前48字节,进行解密即可得到Flag。

解密
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
| #include <iostream> #include <windows.h>
void decrypt(DWORD *data) { DWORD Key[]{0x3c4ed885, 0x12af3e87, 0xd6e1b31f, 0x25c10aa0};
DWORD Delta = 0x9e3779b9;
DWORD d1{}, d2{};
d1 = data[0]; d2 = data[1];
DWORD Sum = Delta * 0xff;
for (int i = 0; i < 0xff; i++) { DWORD tmp1{}, tmp2{}, tmp3{};
tmp1 = ((d1 << 4) ^ (d1 >> 5)) + d1; tmp2 = Key[((Sum >> 9) & 0xC) / 4] + Sum; tmp3 = tmp1 ^ tmp2; d2 -= tmp3;
Sum -= Delta;
tmp1 = ((d2 << 4) ^ (d2 >> 5)) + d2; tmp2 = Key[Sum & 3] + Sum; tmp3 = tmp1 ^ tmp2; d1 -= tmp3; }
data[0] = d1; data[1] = d2; }
int main() { DWORD Enc[]{0x4280669b, 0xd6dfe6bb, 0x56855b5c, 0xc995079b, 0x39e0ba30, 0xbbe402f0, 0x3a9df08b, 0x4dd5f7db, 0x23767dfa, 0x74c33a7f, 0x736805af, 0xff396149};
for (int i = 0; i < 6; i++) { decrypt((DWORD *)((BYTE *)Enc + i * 8)); }
printf("%.48s\n", Enc); return 0; }
|
NepCTF2025{3b213997-d229-42d8-a470-97ebf7d4d029}