CTF-wiki bin.exe 反调试练习
CTF-wiki bin.exe 反调试练习
主函数大致逻辑如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{FILE *v3; // eaxHANDLE v4; // eaxint v11; // [esp+C4h] [ebp-A8h]DWORD v12; // [esp+D4h] [ebp-98h]LPCSTR lpFileName; // [esp+D8h] [ebp-94h]BOOL pbDebuggerPresent; // [esp+DCh] [ebp-90h]int v15; // [esp+E0h] [ebp-8Ch]int v16; // [esp+E4h] [ebp-88h]int i; // [esp+E8h] [ebp-84h]int v18; // [esp+ECh] [ebp-80h]int v19; // [esp+F0h] [ebp-7Ch]char v20[4]; // [esp+F8h] [ebp-74h]int v21; // [esp+108h] [ebp-64h]char v22; // [esp+10Ch] [ebp-60h]char v23; // [esp+10Dh] [ebp-5Fh]CPPEH_RECORD ms_exc; // [esp+154h] [ebp-18h]v22 = 0;memset(&v23, 0, 0x3Fu);v21 = 1;printf("Input password >");v3 = (FILE *)sub_40223D();fgets(&v22, 64, v3);strcpy(v20, "I have a pen.");v21 = strncmp(&v22, v20, 0xDu);if ( !v21 ){puts("Your password is correct.");if ( IsDebuggerPresent() == 1 ) // API反调试{puts("But detected debugger!");exit(1);}if ( sub_401120() == 0x70 ) // 检测NtGloabalFlag{puts("But detected NtGlobalFlag!");exit(1);}v4 = GetCurrentProcess();CheckRemoteDebuggerPresent(v4, &pbDebuggerPresent);if ( pbDebuggerPresent ) // API反调试{printf("But detected remotedebug.\n");exit(1);}v12 = GetTickCount(); // 返回启动到现在的毫秒数for ( i = 0; i == 100; ++i ) // sleep 100毫秒Sleep(1u); // 时间差检测v15 = 1000;if ( GetTickCount() - v12 > 1000 ) // 时间差大于1000 则明显存在调试{printf("But detected debug.\n");exit(1);}lpFileName = "\\\\.\\Global\\ProcmonDebugLogger";if ( CreateFileA("\\\\.\\Global\\ProcmonDebugLogger", 0x80000000, 7u, 0, 3u, 0x80u, 0) != (HANDLE)-1 )// 检测程序ProcessMonitor// 3代表仅在文件存在时打开,如果不存在打开失败返回2{printf("But detect %s.\n", &lpFileName);exit(1);}v11 = sub_401130();if ( v11 == 1 ){printf("But detected Ollydbg.\n");exit(1);}if ( v11 == 2 ){printf("But detected ImmunityDebugger.\n");exit(1);}if ( v11 == 3 ){printf("But detected IDA.\n");exit(1);}if ( v11 == 4 ){printf("But detected WireShark.\n");exit(1);}if ( sub_401240() == 1 ) // 这是 VMware 的一个 "后门"I/O 端口, 0x5658 = "VX". 如果程序在 VMware 内运行, // // 程序使用In指令通过0x5658端口读取数据时, EBX寄存器的值就会变为0x564D5868(0x564D5868 == "VMXh"){printf("But detected VMware.\n");exit(1);}v16 = 1;v19 = 1;v18 = 1 / 0; // 触发异常ms_exc.registration.TryLevel = -2; // 解除异常printf("But detected Debugged.\n");exit(1);}printf("password is wrong.\n");return 0;
}
程序逻辑大致为,输入I have a pen.之后程序进行一些列检测,最后会触发异常。
调试
1.IsDebuggerPresent()
由于strongOD效果对其进行绕过
2.NtGloabalFlag检测
strongOD效果直接绕过
3.CheckRemoteDebuggerPresent()
修改跳转对其进行绕过
4.时间差检测
利用GetTickCount函数来检测其中是否下断点进行调试。
5.ProcessMonitor检测
这里通过检测设备文件\\\\.\\Global\\ProcmonDebugLogger来检测ProcessMonitor
6.进程名检测
利用函数CreateToolhelp32snapshot(2,0)获取快照中所有进程,并依次匹配
7.VMWARE检测
是 VMware 的一个 "后门"I/O 端口, 0x5658 = "VX". 如果程序在 VMware 内运行, 程序使用In指令通过0x5658端口读取数据时, EBX寄存器的值就会变为0x564D5868(0x564D5868 == "VMXh")
8.异常调试
首先调试到触发异常这
然后对KiUserExceptionDispatcher下断点,运行至此。之后进入RtlDispatchException(异常分发函数)
异常调用的流程为VEH->SEH->ZWcontinue
该函数调用了VEH,F7单步进入之后程序并没有调用VEH。继续向下走。遇见函数RtlpExecuteHandlerForException此处内部执行了SEH单步步入该函数,
进入之后再进入ExcuteHandler2
然后进入call ecx 该函数为4019F0,函数反汇编如下,而我们
int __cdecl _except_handler4(PEXCEPTION_RECORD ExceptionRecord, PVOID TargetFrame, int a3)
{PVOID v3; // ebxint *v4; // esiint v5; // eaxchar *v6; // ediint v7; // ecxint v8; // ecxint v9; // ecxint *v10; // eaxint v11; // eaxint v12; // eaxint v13; // ecxint v14; // ecx_DWORD *v16; // eaxint v17; // ecxint v18; // ecxPEXCEPTION_RECORD v19; // [esp+Ch] [ebp-18h]int v20; // [esp+10h] [ebp-14h]int *v21; // [esp+14h] [ebp-10h]int v22; // [esp+18h] [ebp-Ch]int v23; // [esp+1Ch] [ebp-8h]char v24; // [esp+23h] [ebp-1h]v3 = TargetFrame;v4 = (int *)(__security_cookie ^ *((_DWORD *)TargetFrame + 2));v5 = *v4;v24 = 0;v22 = 1;v6 = (char *)TargetFrame + 16;if ( v5 != -2 )v7 = *(_DWORD *)&v6[v5] ^ (unsigned int)&v6[v4[1]];v8 = *(_DWORD *)&v6[v4[2]] ^ (unsigned int)&v6[v4[3]];if ( ExceptionRecord->ExceptionFlags & 0x66 ){
LABEL_25:if ( *((_DWORD *)v3 + 3) == -2 )return v22;_EH4_LocalUnwind(v6, &__security_cookie);}else{*((_DWORD *)TargetFrame - 1) = &v19;v3 = (PVOID)*((_DWORD *)TargetFrame + 3);v19 = ExceptionRecord;v20 = a3;if ( v3 == (PVOID)-2 )return v22;do{v9 = v4[3 * (_DWORD)v3 + 5];v10 = &v4[3 * (_DWORD)v3 + 4];v21 = v10;v11 = *v10;v23 = v11;if ( v9 ){v12 = _EH4_CallFilterFunc(v9, v6);v24 = 1;if ( v12 < 0 ){v22 = 0;goto LABEL_11;}if ( v12 > 0 ){if ( ExceptionRecord->ExceptionCode == -529697949&& dword_40FCBC&& _IsNonwritableInCurrentImage(&dword_40FCBC) ){dword_40FCBC(ExceptionRecord, 1);}_EH4_GlobalUnwind2(TargetFrame, ExceptionRecord);v16 = TargetFrame;if ( *((PVOID *)TargetFrame + 3) != v3 ){_EH4_LocalUnwind(v6, &__security_cookie);v16 = TargetFrame;}v16[3] = v23;if ( *v4 != -2 )v17 = *(_DWORD *)&v6[*v4] ^ (unsigned int)&v6[v4[1]];v18 = *(_DWORD *)&v6[v4[2]] ^ (unsigned int)&v6[v4[3]];_EH4_TransferToHandler(v21[2], v6);goto LABEL_25;}v11 = v23;}v3 = (PVOID)v11;}while ( v11 != -2 );if ( !v24 )return v22;}
LABEL_11:if ( *v4 != -2 )v13 = *(_DWORD *)&v6[*v4] ^ (unsigned int)&v6[v4[1]];v14 = *(_DWORD *)&v6[v4[2]] ^ (unsigned int)&v6[v4[3]];return v22;
}
进入函数继续运行,函数403AD2 为异常过滤
发现异常并没有过滤,继续向下走。执行到403AE9为_EH4_TransferToHandler,进入该函数
开始执行异常处理函数。跳转到4015F6,发现绕过了401611此处的跳转,是比较[ebp-0x88]是否为1,一开始是为1,但是当异常没有忽略交给调试器处理之后解除异常后[ebp-0x88]就会复制为0,所以此处应该是对异常的反调试,开启StrongOD中的skip some Exceptions 即可。或者调试选项忽略异常。

之后又执行了一个貌似反调试函数4012E0
.text:004012E0 sldt eax ;将局部描述符存入eax中
.text:004012E3 retn
检测ldt是否为0如果为0 则没有反调试,不过这里测试了一下无论有没有调试eax则为0。。。。
测试代码:
#include
#includeint main()
{int a = 1;_asm{sldt eax;mov a, eax;}printf("%d", a);system("pause");
}
最后一个地方故意让你不进入一段汇编(解密函数),修改跳转即可
接下来解密函数执行函数,即可看到flag。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
