第33章:隐藏进程-API代码修改技术(下)

前面的方法会频繁的进行脱钩和挂钩操作,并且在多线程环境中容易导致代码错误.因此使用 "热补丁" 技术就很必要了.

热补丁(Hot Patch/Fix ) 又称为7字节代码修改技术.

 

可以看到,前面七个字节并无实际意义.微软使用这种方法就是为了方便在不关机的情况下临时修改库文件,重启时修改的目标库文件会被完全替代.

前五个字节可以被用来当作远跳转 jmp( E9 ) xxxxxxxx ,后两个字节被用作 短跳转 jmp( EB ) xxxx .

 

此种方法可以尽可能的减少对有效代码的修改,使得当线程在修改时,另一个线程仍能正常得执行代码.

只需要在上一节的代码中添加这些代码即可.

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    char            szCurProc[MAX_PATH] = {0,};
    char            *p = NULL;

    GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
    p = strrchr(szCurProc, '\\');
    if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") )
        return TRUE;

    // change privilege
    SetPrivilege(SE_DEBUG_NAME, TRUE);

    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH : 
            // hook
            hook_by_hotpatch("kernel32.dll", "CreateProcessA", 
                             (PROC)NewCreateProcessA);
            hook_by_hotpatch("kernel32.dll", "CreateProcessW", 
                             (PROC)NewCreateProcessW);
            hook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                         (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI); //注意此处,使用的是5字节的代码修改技术
            break;                               // 因为它不像其它 API 被很多进程所使用.

        case DLL_PROCESS_DETACH :
            // unhook
            unhook_by_hotpatch("kernel32.dll", "CreateProcessA");
            unhook_by_hotpatch("kernel32.dll", "CreateProcessW");
            unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                           g_pOrgZwQSI);
            break;
    }

    return TRUE;
}
BOOL unhook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName)   //脱钩
{
    FARPROC pFunc;
    DWORD dwOldProtect;
    PBYTE pByte;
    BYTE pBuf[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 };       //nop *5
    BYTE pBuf2[2] = { 0x8B, 0xFF };              // mov edi,edi


    pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pFunc;
    if( pByte[0] != 0xEB )
        return FALSE;

    VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);     //修改节区5个字节的属性.

    // 1. NOP (0x90)
    memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);
    
    // 2. MOV EDI, EDI (0x8BFF)
    memcpy(pFunc, pBuf2, 2);

    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}
BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew)   //上钩
{
    FARPROC pFunc;
    DWORD dwOldProtect, dwAddress;
    BYTE pBuf[5] = { 0xE9, 0, };
    BYTE pBuf2[2] = { 0xEB, 0xF9 };
    PBYTE pByte;

    pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pFunc;
    if( pByte[0] == 0xEB )
        return FALSE;

    VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);  //修改7个字节的属性

    // 1. NOP (0x90)
    dwAddress = (DWORD)pfnNew - (DWORD)pFunc;   //不用减去本条指令的长度,因为 pFunc 本身就多了5字节
    memcpy(&pBuf[1], &dwAddress, 4);
    memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5);    //写入相应地址.// 2. MOV EDI, EDI (0x8BFF)
    memcpy(pFunc, pBuf2, 2);    //短跳转,相距本条指令的下一条指令7字节

    VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, dwOldProtect, &dwOldProtect);  //恢复属性

    return TRUE;
}
BOOL WINAPI NewCreateProcessA(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
)
{
    BOOL bRet;
    FARPROC pFunc;

    // original API 调用
    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
    pFunc = (FARPROC)((DWORD)pFunc + 2);
    bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,    // ASCII 和 Unicode 的函数版本只有这里不一样.
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

    // 向生成的子进程注入 stealth2.dll
    if( bRet )
        InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);

    return bRet;
}

值得注意的是: 

①并非所有的 API 都支持这个技术.

② ntdll.dll 的 API 代码都比较短,且内部代码地址无依赖性,因此可以将原 API 备份到用户内存区域,然后使用5字节修改技术,修改原 API 的起始部分,使其跳转到备份的 API 执行代码.

posted @ 2020-08-08 16:19  Rev_omi  阅读(210)  评论(0编辑  收藏  举报