首页
社区
课程
招聘
[原创][翻译]反调试:定时
发表于: 2021-6-16 09:38 3885

[原创][翻译]反调试:定时

2021-6-16 09:38
3885

备注
原文地址:https://anti-debug.checkpoint.com/techniques/timing.html
原文标题:Anti-Debug: Timing
更新日期:2021年6月15日
此文后期:根据自身所学进行内容扩充
因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。



目录

  • 定时

  • 1. RDPMC/RDTSC

  • 2. GetLocalTime()

  • 3. GetSystemTime()

  • 4. GetTickCount()

  • 5. ZwGetTickCount() / KiGetTickCount()

  • 6. QueryPerformanceCounter()

  • 7. timeGetTime()

  • 反制措施


定时
当一个进程在调试器中被追踪时,在指令和执行之间会有巨大的延迟。代码的某些部分之间的 "本地 "延迟可以用几种方法测量并与实际延迟进行比较。
1. RDPMC/RDTSC
这些指令要求在CR4寄存器中设置PCE标志寄存器。
RDPMC指令只能在内核模式下使用。
C/C++ 代码:

bool IsDebugged(DWORD64 qwNativeElapsed)
{
    ULARGE_INTEGER Start, End;
    __asm
    {
        xor  ecx, ecx
        rdpmc
        mov  Start.LowPart, eax
        mov  Start.HighPart, edx
    }
    // ... some work
    __asm
    {
        xor  ecx, ecx
        rdpmc
        mov  End.LowPart, eax
        mov  End.HighPart, edx
    }
    return (End.QuadPart - Start.QuadPart) > qwNativeElapsed;
}


RDTSC是一个用户模式指令。
C/C++ 代码:

bool IsDebugged(DWORD64 qwNativeElapsed)
{
    ULARGE_INTEGER Start, End;
    __asm
    {
        xor  ecx, ecx
        rdtsc
        mov  Start.LowPart, eax
        mov  Start.HighPart, edx
    }
    // ... some work
    __asm
    {
        xor  ecx, ecx
        rdtsc
        mov  End.LowPart, eax
        mov  End.HighPart, edx
    }
    return (End.QuadPart - Start.QuadPart) > qwNativeElapsed;
}


2. GetLocalTime()
C/C++ 代码:

bool IsDebugged(DWORD64 qwNativeElapsed)
{
    SYSTEMTIME stStart, stEnd;
    FILETIME ftStart, ftEnd;
    ULARGE_INTEGER uiStart, uiEnd;

    GetLocalTime(&stStart);
    // ... some work
    GetLocalTime(&stEnd);

    if (!SystemTimeToFileTime(&stStart, &ftStart))
        return false;
    if (!SystemTimeToFileTime(&stEnd, &ftEnd))
        return false;

    uiStart.LowPart  = ftStart.dwLowDateTime;
    uiStart.HighPart = ftStart.dwHighDateTime;
    uiEnd.LowPart  = ftEnd.dwLowDateTime;
    uiEnd.HighPart = ftEnd.dwHighDateTime;
    return (uiEnd.QuadPart - uiStart.QuadPart) > qwNativeElapsed;
}


3. GetSystemTime()
C/C++ 代码:

bool IsDebugged(DWORD64 qwNativeElapsed)
{
    SYSTEMTIME stStart, stEnd;
    FILETIME ftStart, ftEnd;
    ULARGE_INTEGER uiStart, uiEnd;

    GetSystemTime(&stStart);
    // ... some work
    GetSystemTime(&stEnd);

    if (!SystemTimeToFileTime(&stStart, &ftStart))
        return false;
    if (!SystemTimeToFileTime(&stEnd, &ftEnd))
        return false;

    uiStart.LowPart  = ftStart.dwLowDateTime;
    uiStart.HighPart = ftStart.dwHighDateTime;
    uiEnd.LowPart  = ftEnd.dwLowDateTime;
    uiEnd.HighPart = ftEnd.dwHighDateTime;
    return (uiEnd.QuadPart - uiStart.QuadPart) > qwNativeElapsed;
}


4. GetTickCount()
C/C++ 代码:

bool IsDebugged(DWORD dwNativeElapsed)
{
    DWORD dwStart = GetTickCount();
    // ... some work
    return (GetTickCount() - dwStart) > dwNativeElapsed;
}


5. ZwGetTickCount() / KiGetTickCount()
这两个函数只在内核模式下使用。

就像用户模式的GetTickCount()或GetSystemTime()一样,内核模式的ZwGetTickCount()从KUSER_SHARED_DATA页面读取。这个页面在虚拟地址的用户模式范围内被映射为只读,在内核范围内被映射为读写。系统时钟的滴答声更新了系统时间,它直接存储在这个页面中。

ZwGetTickCount()的使用方法与GetTickCount()相同。使用KiGetTickCount()比调用ZwGetTickCount()要快,但比直接从KUSER_SHARED_DATA页面读取稍慢一些。
C/C++ 代码:

bool IsDebugged(DWORD64 qwNativeElapsed)
{
    ULARGE_INTEGER Start, End;
    __asm
    {
        int  2ah
        mov  Start.LowPart, eax
        mov  Start.HighPart, edx
    }
    // ... some work
    __asm
    {
        int  2ah
        mov  End.LowPart, eax
        mov  End.HighPart, edx
    }
    return (End.QuadPart - Start.QuadPart) > qwNativeElapsed;
}


6. QueryPerformanceCounter()
C/C++ 代码:

bool IsDebugged(DWORD64 qwNativeElapsed)
{
    LARGE_INTEGER liStart, liEnd;
    QueryPerformanceCounter(&liStart);
    // ... some work
    QueryPerformanceCounter(&liEnd);
    return (liEnd.QuadPart - liStart.QuadPart) > qwNativeElapsed;
}


7. timeGetTime()
C/C++ 代码:

bool IsDebugged(DWORD dwNativeElapsed)
{
    DWORD dwStart = timeGetTime();
    // ... some work
    return (timeGetTime() - dwStart) > dwNativeElapsed;
}


反制措施
调试期间:只需用NOP填充定时检查,并将这些检查的结果设置为适当的值。
对于反调试绕过方案的开发:没有很大的必要去做什么,因为所有的定时检查都不是很可靠。你仍然可以拦截定时函数,加快调用之间的时间。



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 1821
活跃值: (4035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2021-6-17 10:57
0
游客
登录 | 注册 方可回帖
返回
//