前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >API钩取

API钩取

作者头像
红客突击队
发布2022-09-29 21:25:00
8370
发布2022-09-29 21:25:00
举报
文章被收录于专栏:kayden

API钩取

前言

继续学习《逆向工程核心原理》,本篇笔记是第四部分:API钩取,主要介绍了调试钩取、DLL注入实现IAT钩取、API代码修改钩取和全局API钩取等内容

一、API钩取简介

1、基础概念

钩取(Hook):截取信息、更改程序执行流向、添加新功能的技术

使用反汇编/调试器把握程序结构与原理

开发Hook代码,以修改bug、改善程序功能

灵活操作可执行文件和进程内存,设置Hook代码

API(Application Programming Interface):可以认为是调用资源的路径,notepad.exe为例如下图所示

其API调用如下:

API钩取:对Win32 API的钩取,一个例子如下图所示

2、技术图表

(下划线的是常用且好用的方法)

二、调试钩取技术

通过调试钩取技术,来钩取kernel32!WriteFile() API

1、调试器

调试器(Debbuger):能逐一执行被调试者的指令,拥有对寄存器和内存的所有访问权限,工作原理如下图所示

调试事件共9种,见微软官方调试事件 其中,与调试相关的是EXCEPTION_DEBUG_EVENT,与其相关的异常列表如下:

其中,调试器必须处理的是EXCEPTION_BREAKPOINT(断点),对应汇编指令是INT3,IA-32指令是0xCC

要设置断点时,只需将代码在内存中的起始地址的1个字节设置为0xCC即可,想继续调试就把它恢复

2、调试流程

基本思路:被调试者的API起始部份修改为0xCC,控制权转移到调试器后执行指定操作,最后使被调试者重新进入运行状态

对目标进程进行附加操作,使之成为被调试者

Hook:API起始地址的第1个字节修改为0xCC

调用相应API,控制权转移到调试器

执行操作:操作参数、返回值等

脱钩:0xCC恢复原值

运行相应API(正常状态)

Hook:再次修改为0xCC(继续钩取)

控制权返还被调试者

3、示例:记事本 WriteFile() API钩取

目标是将notepad.exe中所有小写字母都变成大写字母

WriteFile() 定义如下:

代码语言:javascript
复制
BOOL WriteFile(
  HANDLE  hFile, //文件句柄
  LPCVOID lpBuffer, //数据缓存区指针
  DWORD   nNumberOfBytesToWrite, //要写的字节数
  LPDWORD lpNumberOfBytesWritten, //用于保存实际写入字节数的存储区域的指针
  LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
);

(1)hookdbg.exe

源码如下:

代码语言:javascript
复制
// hookdbg.exe

#include "windows.h"
#include "stdio.h"

LPVOID g_pfWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;

BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
    // 获取 WriteFile() API 地址(注意是调试进程的内存地址,不是被调试进程)
    g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");

    // API Hook - WriteFile()
    //   更改第一个字节为 0xCC (INT 3) 
    //   (orginal byte 是g_chOrgByte备份)
    memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
    ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 
                      &g_chOrgByte, sizeof(BYTE), NULL);
    WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 
                       &g_chINT3, sizeof(BYTE), NULL);

    return TRUE;
}

BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
    CONTEXT ctx;
    PBYTE lpBuffer = NULL;
    DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
    PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;

    // 断点异常 (INT 3) 
    if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
    {
        // 断点地址为 WriteFile() API 地址
        if( g_pfWriteFile == per->ExceptionAddress )
        {
            // #1. Unhook
            //   0xCC 恢复为 original byte
            WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 
                               &g_chOrgByte, sizeof(BYTE), NULL);

            // #2. 获取线程上下文
            ctx.ContextFlags = CONTEXT_CONTROL;
            GetThreadContext(g_cpdi.hThread, &ctx);

            // #3. 获取 WriteFile() 的 param 2, 3 值
            //   函数参数存在于相应进程的栈
            //   param 2 : ESP + 0x8 缓冲区地址
            //   param 3 : ESP + 0xC 缓冲区大小
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8), 
                              &dwAddrOfBuffer, sizeof(DWORD), NULL);
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC), 
                              &dwNumOfBytesToWrite, sizeof(DWORD), NULL);

            // #4. 分配临时缓冲区
            lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
            memset(lpBuffer, 0, dwNumOfBytesToWrite+1);

            // #5. 复制 WriteFile() 缓冲区到临时缓冲区
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, 
                              lpBuffer, dwNumOfBytesToWrite, NULL);
            printf("\n### original string ###\n%s\n", lpBuffer);

            // #6. 小写字母 -> 大写字母
            for( i = 0; i < dwNumOfBytesToWrite; i++ )
            {
                if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
                    lpBuffer[i] -= 0x20;
            }

            printf("\n### converted string ###\n%s\n", lpBuffer);

            // #7. 变换后的缓冲区复制到 WriteFile() 缓冲区
            WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, 
                               lpBuffer, dwNumOfBytesToWrite, NULL);
            
            // #8. 释放临时缓冲区
            free(lpBuffer);

            // #9. 线程上下文的 EIP 改为 WriteFile() 首地址
            //   (当前为 WriteFile() + 1 位置,INT3命令后)
            ctx.Eip = (DWORD)g_pfWriteFile;
            SetThreadContext(g_cpdi.hThread, &ctx);

            // #10. 运行被调试者
            ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
            Sleep(0);

            // #11. API Hook
            WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 
                               &g_chINT3, sizeof(BYTE), NULL);

            return TRUE;
        }
    }

    return FALSE;
}

void DebugLoop()
{
    DEBUG_EVENT de;
    DWORD dwContinueStatus;

    // 等待被调试者发生事件
    while( WaitForDebugEvent(&de, INFINITE) )
    {
        dwContinueStatus = DBG_CONTINUE; //dwContinueStatus值为DBG_CONTINUE(处理正常)或DBG_EXCEPTION_NOT_HANDLED(无法处理或在SEH中处理)

        // 被调试者生成或附加事件
        if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
        {
            OnCreateProcessDebugEvent(&de); //OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄
        }
        // 异常事件
        else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
        {
            if( OnExceptionDebugEvent(&de) ) //OnExceptionDebugEvent是EXCEPTION_DEBUG_EVENT事件句柄,处理被调试者的INT3指令
                continue;
        }
        // 被调试者终止事件
        else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
        {
            // 被调试者终止 -> debugger 终止
            break;
        }

        // 再次运行被调试者
        ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
    }
}

int main(int argc, char* argv[])
{
    DWORD dwPID;

    if( argc != 2 )
    {
        printf("\nUSAGE : hookdbg.exe <pid>\n");
        return 1;
    }

    // Attach Process
    dwPID = atoi(argv[1]); //以程序运行参数的形式接收进程PID
    if( !DebugActiveProcess(dwPID) ) //将调试器附加到进程上
    {
        printf("DebugActiveProcess(%d) failed!!!\n"
               "Error Code = %d\n", dwPID, GetLastError());
        return 1;
    }

    // 调试器循环
    DebugLoop();

    return 0;
}

(2)试验

先运行notepad.exe,并获取notepad的PID是32220,然后运行hookdbg.exe,如下

输入一串字符串,保存 再次打开的时候会发现都变成大写字母了

三、DLL注入实现IAT钩取技术

本节向计算器calc.exe插入用户的DLL文件,钩取IAT的user32.SetWindowTextW() API地址,使得计算器显示中文数字

1、选定目标API

PEView打开calc.exe,在IAT中寻找API,如下两个负责显示文本

其中,SetDigitemTextW()又调用了SetWindowTextW()

代码语言:javascript
复制
SetWindowTextW() API定义如下:


BOOL SetWindowText(
  HWND hwnd, //窗口句柄
  LPCTSTR lpString //字符串指针(钩取的目标)
);

在OD里验证下

右键-search for-all intermodular calls,在SetWindowTextW()设置断点

运行,然后可以看到第一个断点处lpString的值是0,就是计算器显示的初始值

在计算器中输入7,继续运行,发现lpString的值变为7(注意此时地址不同)

尝试修改为中文“七”,Unicode码4e03,记住是小端序故要逆序 然后就会在计算器上显示“七” 验证完毕

2、IAT钩取工作原理

关于IAT,可以见《逆向工程核心原理》学习笔记(二):PE文件

原理如下图所示:

首先注入hookiat.dll文件,文件中提供了 MySetWindowTextW() 函数

然后修改IAT中的CALL的值为 MySetWindowTextW() 函数起始地址

一系列处理后,再CALL到user32.SetWindowTextW()函数起始地址

user32.SetWindowTextW()执行完后返回到hookiat.dll执行下一条指令

最后返回到01002628

3、示例:计算器显示中文数字

(1)hookiat.dll

代码语言:javascript
复制
// hookiat.dll

#include "stdio.h"
#include "wchar.h"
#include "windows.h"


// typedef
typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);


// globals
FARPROC g_pOrgFunc = NULL;


BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString)
{
    wchar_t* pNum = L"零一二三四五六七八九";
    wchar_t temp[2] = {0,};
    int i = 0, nLen = 0, nIndex = 0;

    nLen = wcslen(lpString);
    for(i = 0; i < nLen; i++)
    {
        // 阿拉伯数字转换为中文数字
        //   lpString 是 wide-character (2 byte) 字符串
        if( L'0' <= lpString[i] && lpString[i] <= L'9' )
        {
            temp[0] = lpString[i];
            nIndex = _wtoi(temp);
            lpString[i] = pNum[nIndex];
        }
    }

    // 调用 user32!SetWindowTextW() API 
    //   (修改 lpString 缓冲区中内容)
    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}


// hook_iat
//   负责钩取 IAT 
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew)
{
  HMODULE hMod;
  LPCSTR szLibName;
  PIMAGE_IMPORT_DESCRIPTOR pImportDesc; 
  PIMAGE_THUNK_DATA pThunk;
  DWORD dwOldProtect, dwRVA;
  PBYTE pAddr;

    //查找 IAT 位置
    // hMod, pAddr = ImageBase of calc.exe
    //             = VA to MZ signature (IMAGE_DOS_HEADER)
  hMod = GetModuleHandle(NULL);
  pAddr = (PBYTE)hMod;

    // pAddr = VA to PE signature (IMAGE_NT_HEADERS)
  pAddr += *((DWORD*)&pAddr[0x3C]);

    // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table
  dwRVA = *((DWORD*)&pAddr[0x80]);

    // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table
  pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);

    // for循环找到user32.dll
  for( ; pImportDesc->Name; pImportDesc++ )
  {
        // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name
    szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);
    if( !_stricmp(szLibName, szDllName) )
    {
            // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk
            //        = VA to IAT(Import Address Table)
      pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + 
                                         pImportDesc->FirstThunk);
            // for循环找到SetWindowTextW的IAT地址
            // pThunk->u1.Function = VA to API
      for( ; pThunk->u1.Function; pThunk++ )
      {
        if( pThunk->u1.Function == (DWORD)pfnOrg )
        {
                    // 更改内存属性为 E/R/W
          VirtualProtect((LPVOID)&pThunk->u1.Function, 
                                   4, 
                                   PAGE_EXECUTE_READWRITE, 
                                   &dwOldProtect);

                    // 修改 IAT 值(钩取)
                    pThunk->u1.Function = (DWORD)pfnNew;
          
                    // 恢复内存属性
                    VirtualProtect((LPVOID)&pThunk->u1.Function, 
                                   4, 
                                   dwOldProtect, 
                                   &dwOldProtect);            

          return TRUE;
        }
      }
    }
  }

  return FALSE;
}



BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
  switch( fdwReason )
  {
    case DLL_PROCESS_ATTACH : 
            // 保存原始 API 地址
             g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), 
                                        "SetWindowTextW");

            // # hook
            //   用 hookiat.MySetWindowText() 钩取 user32.SetWindowTextW() 
      hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
      break;

    case DLL_PROCESS_DETACH :
            // # unhook
            //   将 calc.exe 的 IAT 恢复原值
            hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
      break;
  }

  return TRUE;
}

(2)InjectDll.exe

类似于《逆向工程核心原理》学习笔记(三):DLL注入

代码语言:javascript
复制
// InjectDll.exe

#include "stdio.h"
#include "windows.h"
#include "tlhelp32.h"
#include "winbase.h"
#include "tchar.h"


void usage()
{
  printf("\nInjectDll.exe by ReverseCore\n"
           "- blog  : http://www.reversecore.com\n"
           "- email : reversecore@gmail.com\n\n"
           "- USAGE : InjectDll.exe <i|e> <PID> <dll_path>\n\n");
}


BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
{
  HANDLE hProcess, hThread;
  LPVOID pRemoteBuf;
    DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
  LPTHREAD_START_ROUTINE pThreadProc;

  if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        DWORD dwErr = GetLastError();
    return FALSE;
    }

  pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

  WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);

  pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
  hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  CloseHandle(hThread);
  CloseHandle(hProcess);

  return TRUE;
} 


BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
  BOOL bMore = FALSE, bFound = FALSE;
  HANDLE hSnapshot, hProcess, hThread;
  MODULEENTRY32 me = { sizeof(me) };
  LPTHREAD_START_ROUTINE pThreadProc;

  if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )
    return FALSE;

  bMore = Module32First(hSnapshot, &me);
  for( ;bMore ;bMore = Module32Next(hSnapshot, &me) )
  {
    if( !_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName) )
    {
      bFound = TRUE;
      break;
    }
  }

  if( !bFound )
  {
    CloseHandle(hSnapshot);
    return FALSE;
  }

  if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
  {
    CloseHandle(hSnapshot);
    return FALSE;
  }

  pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary");
  hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  CloseHandle(hThread);
  CloseHandle(hProcess);
  CloseHandle(hSnapshot);

  return TRUE;
}


DWORD _EnableNTPrivilege(LPCTSTR szPrivilege, DWORD dwState)
{
  DWORD dwRtn = 0;
  HANDLE hToken;
  if (OpenProcessToken(GetCurrentProcess(),
    TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
  {
    LUID luid;
    if (LookupPrivilegeValue(NULL, szPrivilege, &luid))
    {
      BYTE t1[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
      BYTE t2[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
      DWORD cbTP = sizeof(TOKEN_PRIVILEGES) + sizeof (LUID_AND_ATTRIBUTES);

      PTOKEN_PRIVILEGES pTP = (PTOKEN_PRIVILEGES)t1;
      PTOKEN_PRIVILEGES pPrevTP = (PTOKEN_PRIVILEGES)t2;

      pTP->PrivilegeCount = 1;
      pTP->Privileges[0].Luid = luid;
      pTP->Privileges[0].Attributes = dwState;

      if (AdjustTokenPrivileges(hToken, FALSE, pTP, cbTP, pPrevTP, &cbTP))
        dwRtn = pPrevTP->Privileges[0].Attributes;
    }

      CloseHandle(hToken);
  }

  return dwRtn;
}


int _tmain(int argc, TCHAR* argv[])
{
    if( argc != 4 )
    {
        usage();
        return 1;
    }

  // adjust privilege
  _EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED);

    // InjectDll.exe <i|e> <PID> <dll_path>
    if( !_tcsicmp(argv[1], L"i") )
        InjectDll((DWORD)_tstoi(argv[2]), argv[3]);
    else if(!_tcsicmp(argv[1], L"e") )
        EjectDll((DWORD)_tstoi(argv[2]), argv[3]);

  return 0;
}

四、API代码修改技术

API代码修改技术:库文件被加载到进程内存后,在目录映像中直接修改要钩取的API代码本身(相较于上一节的IAT钩取,本技术可以钩取更多的不在IAT的API)

本节将用API代码修改技术实现隐藏进程

1、API代码修改原理

原始状态如下:

钩取原理如下图所示:

注入stealth.dll,钩取ntdll.ZwQuerySystemInformation() API

ntdll.ZwQuerySystemInformation() API起始地址的5个字节代码改为stealth.MyZwQuerySystemInformation() 的地址JMP 10001120

于是 ntdll.ZwQuerySystemInformation()被调用的时候,会跳转到stealth.MyZwQuerySystemInformation()

1000116A的指令将 ntdll.ZwQuerySystemInformation() API起始地址的5个字节代码恢复原值

1000119B的指令调用正常的 ntdll.ZwQuerySystemInformation()

ntdll.ZwQuerySystemInformation()执行完后,返回stealth.dll

然后10001212再次钩取ntdll.ZwQuerySystemInformation()(即第二步)

stealth.MyZwQuerySystemInformation() 执行完后,返回进程

2、示例:隐藏进程

(1)相关API

一般获取进程快照,用的是CreateToolHelp32Snapshot() API

枚举进程则用的是EnumProcesses() API

而这两个API底层都调用的是 ntdll.ZwQuerySystemInformation() API

ntdll.ZwQuerySystemInformation() API可以获取运行中的所有进程信息,形成链表

代码语言:javascript
复制
NTSTATUS WINAPI ZwQuerySystemInformation(
  _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,
    _Inout_   PVOID                    SystemInformation,
    _In_      ULONG                    SystemInformationLength,
    _Out_opt_ PULONG                   ReturnLength
);

隐藏进程时,需要钩取所有进程的ZwQuerySystemInformation() API,即全局钩取

(2)源码

HideProc.exe,可以认为是InjectDll.exe的加强版

代码语言:javascript
复制
// HideProc.exe

#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "tchar.h"

typedef void (*PFN_SetProcName)(LPCTSTR szProcName);
enum {INJECTION_MODE = 0, EJECTION_MODE};

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                    &hToken) )
    {
        printf("OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,            // lookup privilege on local system
                              lpszPrivilege,   // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
  HANDLE                  hProcess, hThread;
  LPVOID                  pRemoteBuf;
  DWORD                   dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
  LPTHREAD_START_ROUTINE  pThreadProc;

  if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        printf("OpenProcess(%d) failed!!!\n", dwPID);
    return FALSE;
    }

  pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, 
                                MEM_COMMIT, PAGE_READWRITE);

  WriteProcessMemory(hProcess, pRemoteBuf, 
                       (LPVOID)szDllPath, dwBufSize, NULL);

  pThreadProc = (LPTHREAD_START_ROUTINE)
                  GetProcAddress(GetModuleHandle(L"kernel32.dll"), 
                                 "LoadLibraryW");
  hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 pThreadProc, pRemoteBuf, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

  CloseHandle(hThread);
  CloseHandle(hProcess);

  return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
  BOOL                    bMore = FALSE, bFound = FALSE;
  HANDLE                  hSnapshot, hProcess, hThread;
  MODULEENTRY32           me = { sizeof(me) };
  LPTHREAD_START_ROUTINE  pThreadProc;

  if( INVALID_HANDLE_VALUE == 
        (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )
    return FALSE;

  bMore = Module32First(hSnapshot, &me);
  for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
  {
    if( !_tcsicmp(me.szModule, szDllPath) || 
            !_tcsicmp(me.szExePath, szDllPath) )
    {
      bFound = TRUE;
      break;
    }
  }

  if( !bFound )
  {
    CloseHandle(hSnapshot);
    return FALSE;
  }

  if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
  {
    CloseHandle(hSnapshot);
    return FALSE;
  }

  pThreadProc = (LPTHREAD_START_ROUTINE)
                  GetProcAddress(GetModuleHandle(L"kernel32.dll"), 
                                 "FreeLibrary");
  hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 pThreadProc, me.modBaseAddr, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  CloseHandle(hThread);
  CloseHandle(hProcess);
  CloseHandle(hSnapshot);

  return TRUE;
}

BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
  DWORD                   dwPID = 0;
  HANDLE                  hSnapShot = INVALID_HANDLE_VALUE;
  PROCESSENTRY32          pe;

  // Get the snapshot of the system
  pe.dwSize = sizeof( PROCESSENTRY32 );
  hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

  // find process
  Process32First(hSnapShot, &pe);
  do
  {
    dwPID = pe.th32ProcessID;
    
    if( dwPID < 100 ) //PID小于100的系统进程不注入
      continue;

        if( nMode == INJECTION_MODE )
        InjectDll(dwPID, szDllPath); //dll注入
        else
            EjectDll(dwPID, szDllPath); //卸载dll
  } 
  while( Process32Next(hSnapShot, &pe) );

  CloseHandle(hSnapShot);

  return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
    int                     nMode = INJECTION_MODE;
    HMODULE                 hLib = NULL;
    PFN_SetProcName         SetProcName = NULL;

  if( argc != 4 ) //检验参数
  { 
    printf("\n Usage  : HideProc.exe <-hide|-show> "\
               "<process name> <dll path>\n\n");
    return 1;
  }

  // change privilege
    SetPrivilege(SE_DEBUG_NAME, TRUE);

    // load library
    hLib = LoadLibrary(argv[3]);

    // set process name to hide
    SetProcName = (PFN_SetProcName)GetProcAddress(hLib, "SetProcName");
    SetProcName(argv[2]);

    // Inject(Eject) Dll to all process
    if( !_tcsicmp(argv[1], L"-show") )
      nMode = EJECTION_MODE;

    InjectAllProcess(nMode, argv[3]);

    // free library
    FreeLibrary(hLib);

  return 0;
}

stealth.dll

代码语言:javascript
复制
// stealth.dll

#include "windows.h"
#include "tchar.h"

#define STATUS_SUCCESS            (0x00000000L) 

typedef LONG NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemProcessInformation = 5,
    SystemProcessorPerformanceInformation = 8,
    SystemInterruptInformation = 23,
    SystemExceptionInformation = 33,
    SystemRegistryQuotaInformation = 37,
    SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    PVOID Reserved2[3];
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)
                 (SYSTEM_INFORMATION_CLASS SystemInformationClass, 
                  PVOID SystemInformation, 
                  ULONG SystemInformationLength, 
                  PULONG ReturnLength);

#define DEF_NTDLL                       ("ntdll.dll")
#define DEF_ZWQUERYSYSTEMINFORMATION    ("ZwQuerySystemInformation")


// global variable (in sharing memory)
#pragma comment(linker, "/SECTION:.SHARE,RWS") 
#pragma data_seg(".SHARE")
    TCHAR g_szProcName[MAX_PATH] = {0,};
#pragma data_seg()

// global variable
BYTE g_pOrgBytes[5] = {0,};

//此函数用来将api前5个字节改为jmp xxxx,以实现钩取
//szDllName:dll名称,szFuncName:api名称,pfnNew:勾取函数地址,pOrgBytes:存储原来5个字节的缓冲区
BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
    FARPROC pfnOrg;
    DWORD dwOldProtect, dwAddress;
    BYTE pBuf[5] = {0xE9, 0, };
    PBYTE pByte;

    // 获取需要勾取的API地址
    pfnOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName); 
    pByte = (PBYTE)pfnOrg;

    // 若已被勾取,则返回False
    if( pByte[0] == 0xE9 )
        return FALSE;

    // 为了修改5字节,先向内存添加“写”的属性
    VirtualProtect((LPVOID)pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // 备份原有代码(5字节)
    memcpy(pOrgBytes, pfnOrg, 5);

    // 计算 JMP 地址 (E9 XXXX)
    // => XXXX = pfnNew - pfnOrg - 5
    dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5;
    memcpy(&pBuf[1], &dwAddress, 4);

    // Hook - 修改 5 byte  (JMP XXXX)
    memcpy(pfnOrg, pBuf, 5);

    // 恢复内存属性
    VirtualProtect((LPVOID)pfnOrg, 5, dwOldProtect, &dwOldProtect);
    
    return TRUE;
}


BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
    FARPROC pFunc;
    DWORD dwOldProtect;
    PBYTE pByte;

    // 获取 API 地址
    pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
    pByte = (PBYTE)pFunc;

    // 若已脱钩,则返回False
    if( pByte[0] != 0xE9 )
        return FALSE;

    // 向内存添加“写”的属性,为恢复原代码做准备
    VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);

    // Unhook
    memcpy(pFunc, pOrgBytes, 5);

    // 恢复内存属性
    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}

// 勾取过程
NTSTATUS WINAPI NewZwQuerySystemInformation(
                SYSTEM_INFORMATION_CLASS SystemInformationClass, 
                PVOID SystemInformation, 
                ULONG SystemInformationLength, 
                PULONG ReturnLength)
{
    NTSTATUS status;
    FARPROC pFunc;
    PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
    char szProcName[MAX_PATH] = {0,};
    
    // 开始前先“脱钩”
    unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes);

    // 调用原始API
    pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL), 
                           DEF_ZWQUERYSYSTEMINFORMATION);
    status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
              (SystemInformationClass, SystemInformation, 
              SystemInformationLength, ReturnLength);

    if( status != STATUS_SUCCESS )
        goto __NTQUERYSYSTEMINFORMATION_END;

    // 针对 SystemProcessInformation 类型操作
    if( SystemInformationClass == SystemProcessInformation )
    {
        // SYSTEM_PROCESS_INFORMATION 类型转换
        // pCur 是单向链表的头
        pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

        while(TRUE)
        {
            // 比较进程名称
            // g_szProcName为要隐藏的进程的名称
            // (=在SetProcName()设置)
            if(pCur->Reserved2[1] != NULL)
            {
                if(!_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName))
                {
                    // 从链表中删除隐藏进程的节点
                    if(pCur->NextEntryOffset == 0)
                        pPrev->NextEntryOffset = 0;
                    else
                        pPrev->NextEntryOffset += pCur->NextEntryOffset;
                }
                else    
                    pPrev = pCur;
            }

            if(pCur->NextEntryOffset == 0)
                break;

            // 链表的下一项
            pCur = (PSYSTEM_PROCESS_INFORMATION)
                    ((ULONG)pCur + pCur->NextEntryOffset);
        }
    }

__NTQUERYSYSTEMINFORMATION_END:

    // 函数终止前,再次执行API勾取操作,为下次调用准备
    hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, 
                 (PROC)NewZwQuerySystemInformation, g_pOrgBytes);

    return status;
}


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

    // #1. 异常处理
    // 若当前进程为 HookProc.exe 则不进行钩取
    GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
    p = strrchr(szCurProc, '\\');
    if( (p != NULL) && !_stricmp(p+1, "HideProc.exe") )
        return TRUE;

    switch( fdwReason )
    {
        // #2. API Hooking
        case DLL_PROCESS_ATTACH : 
        hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, 
                     (PROC)NewZwQuerySystemInformation, g_pOrgBytes);
        break;

        // #3. API Unhooking 
        case DLL_PROCESS_DETACH :
        unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, 
                       g_pOrgBytes);
        break;
    }

    return TRUE;
}


#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void SetProcName(LPCTSTR szProcName)
{
    _tcscpy_s(g_szProcName, szProcName);
}
#ifdef __cplusplus
}
#endif

五、全局API钩取

全局 API 钩取针对所有进程,包括正在运行和将要运行的进程

用全局API钩取对上一节内容进行加强

1、相关API

(1)Kernel32.CreateProcess()

Kernel32.CreateProcess() API 是用来创建新进程的,钩取时要注意:

要同时钩取Kernel32.CreateProcessA()和Kernel32.CreateProcessW()

上两个API分别调用了CreateProcessInternalA()和CreateProcessInternalW(),故这两个API也要钩取

代码语言:javascript
复制
BOOL CreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

可参考CreateProcess函数详解

(2)Ntdll.ZwResumeThread()

Ntdll.ZwResumeThread()在进程创建后,主线程运行前在Kernel32.CreateProcess() 内被调用执行,不过这个API似乎并未公开,故有不确定性

代码语言:javascript
复制
ZwResumeThread(
  IN  HANDLE  ThreadHandle,
  OUT  PULONG  SuspendCount OPTIONAL
)

2、源码

(1)HideProc2.exe

HideProc2.exe与HideProc.exe相似

代码语言:javascript
复制
// HideProc2.exe

#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "tchar.h"

enum {INJECTION_MODE = 0, EJECTION_MODE};

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                    &hToken) )
    {
        printf("OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,            // lookup privilege on local system
                              lpszPrivilege,   // privilege to lookup 
                              &luid) )         // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
  HANDLE                  hProcess, hThread;
  LPVOID                  pRemoteBuf;
  DWORD                   dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
  LPTHREAD_START_ROUTINE  pThreadProc;

  if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        printf("OpenProcess(%d) failed!!!\n", dwPID);
    return FALSE;
    }

  pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, 
                                MEM_COMMIT, PAGE_READWRITE);

  WriteProcessMemory(hProcess, pRemoteBuf, 
                       (LPVOID)szDllPath, dwBufSize, NULL);

  pThreadProc = (LPTHREAD_START_ROUTINE)
                  GetProcAddress(GetModuleHandle(L"kernel32.dll"), 
                                 "LoadLibraryW");
  hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 pThreadProc, pRemoteBuf, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

  CloseHandle(hThread);
  CloseHandle(hProcess);

  return TRUE;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
  BOOL                    bMore = FALSE, bFound = FALSE;
  HANDLE                  hSnapshot, hProcess, hThread;
  MODULEENTRY32           me = { sizeof(me) };
  LPTHREAD_START_ROUTINE  pThreadProc;

  if( INVALID_HANDLE_VALUE == 
        (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )
    return FALSE;

  bMore = Module32First(hSnapshot, &me);
  for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
  {
    if( !_tcsicmp(me.szModule, szDllPath) || 
            !_tcsicmp(me.szExePath, szDllPath) )
    {
      bFound = TRUE;
      break;
    }
  }

  if( !bFound )
  {
    CloseHandle(hSnapshot);
    return FALSE;
  }

  if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
  {
    printf("OpenProcess(%d) failed!!!\n", dwPID);
    CloseHandle(hSnapshot);
    return FALSE;
  }

  pThreadProc = (LPTHREAD_START_ROUTINE)
                  GetProcAddress(GetModuleHandle(L"kernel32.dll"), 
                                 "FreeLibrary");
  hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 pThreadProc, me.modBaseAddr, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  CloseHandle(hThread);
  CloseHandle(hProcess);
  CloseHandle(hSnapshot);

  return TRUE;
}

BOOL InjectAllProcess(int nMode, LPCTSTR szDllPath)
{
  DWORD                   dwPID = 0;
  HANDLE                  hSnapShot = INVALID_HANDLE_VALUE;
  PROCESSENTRY32          pe;

  // Get the snapshot of the system
  pe.dwSize = sizeof( PROCESSENTRY32 );
  hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

  // find process
  Process32First(hSnapShot, &pe);
  do
  {
    dwPID = pe.th32ProcessID;

    if( dwPID < 100 )
      continue;

        if( nMode == INJECTION_MODE )
        InjectDll(dwPID, szDllPath);
        else
            EjectDll(dwPID, szDllPath);
  } while( Process32Next(hSnapShot, &pe) );

  CloseHandle(hSnapShot);

  return TRUE;
}

int _tmain(int argc, TCHAR* argv[])
{
    int nMode = INJECTION_MODE;

  if( argc != 3 )
  {
    printf("\n Usage  : HideProc2.exe <-hide|-show> <dll path>\n\n");
    return 1;
  }

  // change privilege
  SetPrivilege(SE_DEBUG_NAME, TRUE);

    // Inject(Eject) Dll to all process
    if( !_tcsicmp(argv[1], L"-show") )
      nMode = EJECTION_MODE;

    InjectAllProcess(nMode, argv[2]);

  return 0;
}

(2)stealth2.dll

使用时先将stealth2.dll复制到%SYSTEM%文件夹

代码语言:javascript
复制
#include "windows.h"
#include "stdio.h"
#include "tchar.h"

#define STR_MODULE_NAME          (L"stealth2.dll")
#define STR_HIDE_PROCESS_NAME      (L"notepad.exe")
#define STATUS_SUCCESS          (0x00000000L) 

typedef LONG NTSTATUS;

typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemTimeOfDayInformation = 3,
    SystemProcessInformation = 5,
    SystemProcessorPerformanceInformation = 8,
    SystemInterruptInformation = 23,
    SystemExceptionInformation = 33,
    SystemRegistryQuotaInformation = 37,
    SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    BYTE Reserved1[52];
    PVOID Reserved2[3];
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)(
    SYSTEM_INFORMATION_CLASS SystemInformationClass, 
    PVOID SystemInformation, 
    ULONG SystemInformationLength, 
    PULONG ReturnLength);

typedef BOOL (WINAPI *PFCREATEPROCESSA)(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
);

typedef BOOL (WINAPI *PFCREATEPROCESSW)(
    LPCTSTR lpApplicationName,
    LPTSTR lpCommandLine,
    LPSECURITY_ATTRIBUTES lpProcessAttributes,
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCTSTR lpCurrentDirectory,
    LPSTARTUPINFO lpStartupInfo,
    LPPROCESS_INFORMATION lpProcessInformation
);

BYTE g_pOrgCPA[5] = {0,};
BYTE g_pOrgCPW[5] = {0,};
BYTE g_pOrgZwQSI[5] = {0,};

BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
  FARPROC pFunc;
  DWORD dwOldProtect, dwAddress;
  BYTE pBuf[5] = {0xE9, 0, };
  PBYTE pByte;

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

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

  memcpy(pOrgBytes, pFunc, 5);

  dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5;
  memcpy(&pBuf[1], &dwAddress, 4);

  memcpy(pFunc, pBuf, 5);

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

  return TRUE;
}

BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
  FARPROC pFunc;
  DWORD dwOldProtect;
  PBYTE pByte;

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

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

  memcpy(pFunc, pOrgBytes, 5);

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

  return TRUE;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                    &hToken) )
    {
        printf("OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,             // lookup privilege on local system
                              lpszPrivilege,    // privilege to lookup 
                              &luid) )          // receives LUID of privilege
    {
        printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

// 这个InjectDll2值得注意下
BOOL InjectDll2(HANDLE hProcess, LPCTSTR szDllName)
{
  HANDLE hThread;
  LPVOID pRemoteBuf;
  DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
  FARPROC pThreadProc;

  pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, 
                                MEM_COMMIT, PAGE_READWRITE);
    if( pRemoteBuf == NULL )
        return FALSE;

  WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, 
                       dwBufSize, NULL);

  pThreadProc = GetProcAddress(GetModuleHandleA("kernel32.dll"), 
                                 "LoadLibraryW");
  hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 (LPTHREAD_START_ROUTINE)pThreadProc, 
                                 pRemoteBuf, 0, NULL);
  WaitForSingleObject(hThread, INFINITE);  

  VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

  CloseHandle(hThread);

  return TRUE;
}

NTSTATUS WINAPI NewZwQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass, 
  PVOID SystemInformation, 
  ULONG SystemInformationLength, 
  PULONG ReturnLength)
{
  NTSTATUS status;
  FARPROC pFunc;
  PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
  char szProcName[MAX_PATH] = {0,};

  unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);

  pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"), 
                           "ZwQuerySystemInformation");
  status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
             (SystemInformationClass, SystemInformation, 
              SystemInformationLength, ReturnLength);

  if( status != STATUS_SUCCESS )
    goto __NTQUERYSYSTEMINFORMATION_END;

  if( SystemInformationClass == SystemProcessInformation )
  {
    pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;

    while(TRUE)
    {
            if(pCur->Reserved2[1] != NULL)
            {
                if(!_tcsicmp((PWSTR)pCur->Reserved2[1], STR_HIDE_PROCESS_NAME))
          {
            if(pCur->NextEntryOffset == 0)
              pPrev->NextEntryOffset = 0;
            else
              pPrev->NextEntryOffset += pCur->NextEntryOffset;
          }
          else    
            pPrev = pCur;  
            }

      if(pCur->NextEntryOffset == 0)
        break;

      pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
    }
  }

__NTQUERYSYSTEMINFORMATION_END:

  hook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                 (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);

  return status;
}

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;

    // unhook
    unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);

    // 调用 original API 
    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
    bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

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

    // hook
    hook_by_code("kernel32.dll", "CreateProcessA", 
                 (PROC)NewCreateProcessA, g_pOrgCPA);

    return bRet;
}

BOOL WINAPI NewCreateProcessW(
    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;

    // unhook
    unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW);

    // 调用 original API 
    pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW");
    bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName,
                                     lpCommandLine,
                                     lpProcessAttributes,
                                     lpThreadAttributes,
                                     bInheritHandles,
                                     dwCreationFlags,
                                     lpEnvironment,
                                     lpCurrentDirectory,
                                     lpStartupInfo,
                                     lpProcessInformation);

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

    // hook
    hook_by_code("kernel32.dll", "CreateProcessW", 
                (PROC)NewCreateProcessW, g_pOrgCPW);

    return bRet;
}

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

    // 异常处理使注入不会发生在 HideProc2.exe 进程
    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_code("kernel32.dll", "CreateProcessA", 
                         (PROC)NewCreateProcessA, g_pOrgCPA);
            hook_by_code("kernel32.dll", "CreateProcessW", 
                         (PROC)NewCreateProcessW, g_pOrgCPW);
            hook_by_code("ntdll.dll", "ZwQuerySystemInformation", 
                         (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
            break;

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

    return TRUE;
}

结语

本章对API钩取做了学习 书中还提点了下工具的使用标准


红客突击队于2019年由队长k龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 红客突击队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • API钩取
    • 2、技术图表
    • 二、调试钩取技术
      • 1、调试器
        • (1)hookdbg.exe
        • (2)试验
      • 3、示例:计算器显示中文数字
        • (1)hookiat.dll
        • (2)InjectDll.exe
    • 四、API代码修改技术
      • 1、API代码修改原理
        • (2)源码
        • (2)Ntdll.ZwResumeThread()
      • 2、源码
        • (1)HideProc2.exe
        • (2)stealth2.dll
    • 结语
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档