VMProtect分析-加载器

分析对象为网上流传的VMP源码

源码路径 函数名 行号
runtime\loader.cc SetupImage 884

1、反调试

1.1 用户模式

line:17492通过检测PEB->BeingDebugged,常规手段了,没什么好说。

image-20230614144109419

然后就是通过NtSetInformationProcessNtQueryInformationProcessNtSetInformationThread进行反调试,各功能如下:

  • NtSetInformationProcess:如果是window10则调用,参数二为ProcessInstrumentationCallback。这个参数主要是注册一个回调,在Windows系统Vista以及之后的版本中,可以使用KPROCESS->InstrumentationCallback来指定回调函数的地址,每次函数从内核态返回用户态之后系统都会调用指定的回调函数。但是这里VMP设置了一个NULL的值,猜测可能是有人利用了这种机制来进行绕过反调试,因此清空这个位置。
    image-20230614153757178

    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
    //https://pastebin.com/9TqRGsM5
    //https://secrary.com/Random/InstrumentationCallback/

    //------64-Bit---------------
    #define ProcessInstrumentationCallback 0x28
    extern "C"
    {
    void DbgBreakPoint();
    int __stdcall ZwSetInformationProcess(HANDLE,unsigned long long,unsigned long long*,unsigned long long);
    }

    void* BeingDebugged()
    {
    unsigned long long Cano= 0;
    ZwSetInformationProcess(GetCurrentProcess(),ProcessInstrumentationCallback,&Cano,0x8);
    MessageBox(0,L"Being Debugged\r\n",L"waliedassar",0);
    ExitProcess(0);
    }
    int main()
    {
    unsigned long long Cano= (unsigned long long)&BeingDebugged;
    int ret=ZwSetInformationProcess(GetCurrentProcess(),ProcessInstrumentationCallback,&Cano,0x8);
    if(ret==0xC0000061) printf("Expected\r\n");
    return 0;
    }
  • NtQueryInformationProcess:查询ProcessDebugPortProcessDebugObjectHandle
    image-20230614155347062

  • NtSetInformationThread:传入ThreadHideFromDebugger隐藏线程,也是老常规了。
    image-20230614155939314

最后利用异常机制检测反调试。

image-20230615140914317

如果存在调试器则会继续执行__writeeflags之后的代码,否则会进入自己的异常函数执行。

1.2 内核模式

内核模式本质上还是跟用户模式没什么区别,主要是调用NtQuerySystemInformation以查询系统为主,拦截双机调试为主。

  • SystemKernelDebuggerInformation:通过传入SystemKernelDebuggerInformation查询DebuggerEnabledDebuggerNotPresent是否有值。
    image-20230614160245866

  • SystemModuleInformation:通过判断是否存在一些调试设备。这里可以去看我反调试那篇文章,符号的那一节。

    1
    2
    3
    4
    5
    \\.\SICE 
    \\.\SIWVID
    \\.\NTICE
    \\.\ICEEXT
    \\.\SYSER

    image-20230614162012174

1.3 API检测

检测头部是否为0xCC,对抗软件断点。

image-20230615150745485

1.4 内存检测

VMP会查询整个镜像模块中原可执行的代码是否被设置为了无权限,对抗内存断点。

image-20230615150835541

2、检测虚拟机

2.1 CPUID

老常规,cpuid

image-20230615103046007

首先使用eax=1参数调用cpuid检测ecx的31位是否为1来检测虚拟机。(虚拟机下为1)

image-20230615103128257

然后在进行eax=0x40000000获取CPU的信息,判断字符串是否为正常字符串。

image-20230615103243845

2.3 系统固件

image-20230615143818429

通过EnumSystemFirmwareTablesGetSystemFirmwareTable 判断原始固件表是否存在虚拟机的名字。

image-20230615143924614

检测的内容有

1
2
3
4
5
6
QEMU
Microsoft
innotek
VirtuaiBox
VMware
Parallels

如果上述函数无法调用,则通过打开设备\\device\\physicalmemory来检查是否存在上述固件名字

image-20230615144654549

2.4 检测固定DLL

检测SBIEDLL.DLL是否存在,这个DLL是沙箱(Sandboxie)的一个组件。

image-20230615150029661

总结

只是分析了基于windows反调试和反虚拟机,linux和mac方面的也大吃差不多。至于内存CRC校验的东西就不看啦!

对于检测虚拟机和调试基本上就是比较常规的东西,但还是有意外收货!!!!

一个是KPROCESS->InstrumentationCallback这个标志位,这个可能在之后编程的时候会用到,觉得非常牛逼,嘎嘎嘎!!!!