您的位置:首页 > 其它

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

2017-02-01 18:05 537 查看
我们通过一个示例来练习在ProcExp.exe和taskmgr.exe中隐藏进程。钩取前后的原理图如下所示。





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



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

HideProc.cpp

#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中。

// 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钩取。
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)。
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函数类似。
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。隐藏某进程之前,先要查找与之对应的链表成员,然后断开其与链表的链接。
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;
}
完整的代码如下。
#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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: