台州市赛 Reverse 方向
nanomites
主函数
查看函数 sub_40195C
这里使用了Windows API中的CreateProcessA
和WaitForDebugEvent
等函数。创建了一个新的进程并进入调试模式,通过处理调试事件(如创建线程、异常、退出等)来控制进程的执行
这里利用 dwDebugEventCode
来执行程序逻辑
常见的 dwDebugEventCode
事件类型及其对应的常量值如下:
EXCEPTION_DEBUG_EVENT
(值:1
):- 表示一个异常发生了。调试器可以捕获各种类型的异常,包括访问冲突、除零、断点异常等。异常信息保存在
DEBUG_EVENT.u.Exception
中。
- 表示一个异常发生了。调试器可以捕获各种类型的异常,包括访问冲突、除零、断点异常等。异常信息保存在
CREATE_THREAD_DEBUG_EVENT
(值:2
):- 表示被调试的进程创建了一个新的线程。调试器可以通过该事件获取新线程的句柄。线程信息保存在
DEBUG_EVENT.u.CreateThread
中。
- 表示被调试的进程创建了一个新的线程。调试器可以通过该事件获取新线程的句柄。线程信息保存在
CREATE_PROCESS_DEBUG_EVENT
(值:3
):- 表示被调试的进程启动。此事件发生在调试器启动的进程开始运行时。进程信息(如句柄)保存在
DEBUG_EVENT.u.CreateProcessInfo
中。
- 表示被调试的进程启动。此事件发生在调试器启动的进程开始运行时。进程信息(如句柄)保存在
EXIT_THREAD_DEBUG_EVENT
(值:4
):- 表示一个线程已经退出。调试器可以使用这个事件来清理和跟踪已退出的线程。退出信息保存在
DEBUG_EVENT.u.ExitThread
中。
- 表示一个线程已经退出。调试器可以使用这个事件来清理和跟踪已退出的线程。退出信息保存在
EXIT_PROCESS_DEBUG_EVENT
(值:5
):- 表示被调试的进程已经退出。调试器可以使用这个事件来获取进程的退出码并进行清理工作。退出信息保存在
DEBUG_EVENT.u.ExitProcess
中。
- 表示被调试的进程已经退出。调试器可以使用这个事件来获取进程的退出码并进行清理工作。退出信息保存在
LOAD_DLL_DEBUG_EVENT
(值:6
):- 表示被调试的进程加载了一个新的动态链接库(DLL)。调试器可以使用这个事件来获取DLL的句柄和路径。DLL信息保存在
DEBUG_EVENT.u.LoadDll
中。
- 表示被调试的进程加载了一个新的动态链接库(DLL)。调试器可以使用这个事件来获取DLL的句柄和路径。DLL信息保存在
核心加密逻辑是 case1 那个 EXCEPTION_DEBUG_EVENT
后面会讲到,我们一步一步看
我们看另外一个函数 sub_401C38
这里有一个运行时载入代码,我们把 unk_412020 导出然后 使用 ida 分析
可以看到主要逻辑是把传入的输入的数据加载一个字节到 r12 寄存器,然后对 r11 寄存器进行复制异或和循环位移,然后关键是 ud2 指令,这个是抛出异常,再联想到上面 EXCEPTION_DEBUG_EVENT
就可以知道加密逻辑了
sub_401584
是加密函数,密文在 r11 寄存器中,然后只比较 r13 寄存器为 1 的情况,上面那么多 r13 为 0 的情况是假的
把上面 dump 出来的热加载的 shellcode disasm 然后用 python 提取出含有 mov r13, 1
的片段,然后我们就得计算出每个片段对应的 r11 值,也就是密文,这里考虑到精度,使用 unicorn 计算比较好
1 | from unicorn import * |
得到密文表
1 | [0x84db9614, 0x174760d3, 0x7ac80e2c, 0x3194ec2e, 0x70a549c3, 0x41dedf66, 0x7f69c81e, 0x37b76e13, 0x37b76e13, 0x41dedf66, 0xb99d68d8, 0xcfef5b0b, 0x174760d3, 0xb78ac2e7, 0xea1b9f56, 0xee54ef8e, 0x174760d3, 0xb99d68d8, 0xf2475372, 0xdc310a37, 0xee54ef8e, 0x37b76e13, 0x3194ec2e, 0x37b76e13, 0xea1b9f56, 0xee54ef8e, 0xb99d68d8, 0xea1b9f56, 0xb78ac2e7, 0x9d07d8da, 0xee54ef8e, 0x41dedf66, 0x8288d321, 0x174760d3, 0x9d07d8da, 0x174760d3, 0x8288d321, 0xdc310a37, 0x45e26648, 0x41dedf66, 0x8288d321, 0x8288d321, 0x930b26e3, 0xabef6fef] |
观察异常处理中的加密函数
是一系列较为复杂的位移操作,应该是可以解的,但是笔者采取了一个投巧的方法,用x64dbg下断在 0x0000000000401862
由于可视字符太多,所以笔者写了一个脚本来自动打印到日志
1 | run |
然后在flag输入处输入全可打印字符
1 | 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ |
提取出对应表
1 | {2596959767: '!', 3278025627: '"', 1199340385: '#', 3652842418: '$', 3416942461: '%', 723666731: '&', 280982008: "'", 3348210159: '(', 1029965590: ')', 1646217348: '*', 1509106344: '+', 1884266855: ',', 3998543758: '-', 10365811: '.', 3246951466: '/', 4064760690: '0', 3927678806: '1', 2190005025: '2', 2634537178: '3', 3418004561: '4', 3079324391: '5', 3488570123: '6', 1172465224: '7', 2466981603: '8', 3694201399: '9', 1612783687: ':', 693140910: ';', 3726077530: '<', 1307370518: '=', 726644641: '>', 1786144152: '?', 2537538325: '@', 390553811: 'A', 934768147: 'B', 831843374: 'C', 2228983316: 'D', 3114100952: 'E', 1105125222: 'F', 2398680771: 'G', 3353174339: 'H', 3250317036: 'I', 870428025: 'J', 3051147020: 'K', 120575263: 'L', 1208782285: 'M', 2708701790: 'N', 84861922: 'O', 2027386979: 'P', 1073431481: 'Q', 867118355: 'R', 2059931180: 'S', 1889880515: 'T', 4144402592: 'U', 157475374: 'V', 3597550946: 'W', 3904163235: 'X', 3052880830: 'Y', 1826329493: 'Z', 3635011357: '[', 2712232590: '\\', 2987677768: ']', 2851724154: '^', 2131826772: '_', 2472816263: '`', 291415938: 'a', 1891737825: 'b', 4106698431: 'c', 51373921: 'd', 3596199336: 'e', 86081972: 'f', 429896102: 'g', 1213478405: 'h', 1178941954: 'i', 2136545382: 'j', 1319470528: 'k', 2682404089: 'l', 2376513170: 'm', 3465855092: 'n', 1867828354: 'o', 2685652659: 'p', 1457933662: 'q', 3227490855: 'r', 3060405360: 's', 1697040329: 't', 294797628: 'u', 2577271396: 'v', 1420360541: 'w', 260209567: 'x', 2851528244: 'y', 2240464916: 'z', 2137638942: '{', 808490953: '|', 2884595695: '}', 3702355519: '~'} |
然后一一查询,就可以得到flag DASCTF{BBFE6A51-AE09-BCB1-E153-F2A3A297F228}