天天看點

VC++程序注入

概述:按照個人了解的來概述就是,将我們自己的一個dll注入到目标程序中去。這樣目标程序就運作了我們的dll,在我們的dll中劫持目标函數的跳轉指令----也就是目标程序在執行某一個函數時被我們攔截了,改成執行我們的函數。 詳細:

本教程邊寫代碼邊解釋,運作環境VS2012+Qt

源碼連結http://download.csdn.net/download/sky_calls/10264654

第一步、建立一個目标程序TargetProcess

其實就是一個很簡單的寫序列槽操作 建立過程省略,代碼如下

關鍵函數解釋

void TargetProcess::writeCom() { HANDLE hCom; //序列槽句柄 hCom=CreateFile(L"COM1",//COM口 GENERIC_READ|GENERIC_WRITE, //允許讀和寫 0, //獨占方式 NULL, OPEN_EXISTING, //打開而不是建立 0, //同步方式 NULL); if(hCom==(HANDLE)-1) return;

SetupComm(hCom,1024,1024); //輸出緩沖區的大小是1024 COMMTIMEOUTS TimeOuts; //設定讀逾時 TimeOuts.ReadIntervalTimeout=1000; TimeOuts.ReadTotalTimeoutMultiplier=500; TimeOuts.ReadTotalTimeoutConstant=5000; //設定寫逾時 TimeOuts.WriteTotalTimeoutMultiplier=500; TimeOuts.WriteTotalTimeoutConstant=2000; SetCommTimeouts(hCom,&TimeOuts); //設定逾時 DCB dcb; GetCommState(hCom,&dcb); dcb.BaudRate=9600; //波特率為9600 dcb.ByteSize=8; //每個位元組有8位 dcb.Parity='\0'; //無奇偶校驗位 dcb.StopBits='\0'; //兩個停止位 SetCommState(hCom,&dcb); PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

//同步寫序列槽 char lpOutBuffer[100]; QString tmpContent = "hello com1"; memcpy_s(lpOutBuffer, 100, tmpContent.toStdString().c_str(), tmpContent.length()); lpOutBuffer[tmpContent.length()] = 0; DWORD dwBytesWrite=100; COMSTAT ComStat; DWORD dwErrorFlags; BOOL bWriteStat; ClearCommError(hCom,&dwErrorFlags,&ComStat); bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);

PurgeComm(hCom, PURGE_TXABORT| PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

CloseHandle(hCom); }

第二步、建立要注入的dll工程 ApiHook

要注入的這個dll庫必須有DllMain函數作為入口,否則dll裡面的劫持操作無法完成 這裡我參考了 http://www.cokco.cn/thread-9360-1-1.html 關鍵代碼如下 typedef struct 

{

LPCSTR lpFunctionName; // name of api 要攔截的函數名字

LPCTSTR lpDllName; // name of dll that has the api要攔截函數所屬dll

LPVOID lpRecallfn; // recall function address 自定義的替換函數

LPVOID lpApiAddr; // api address 原始函數位址

PBYTE pOrgfnMem; // memory to save first few bytes of api and execute jmp code 原始函數跳轉指令

int nOrgfnMemSize; // size of pOrgfnMem 跳轉指令的大小

} RECALL_API_INFO, *PRECALL_API_INFO;

// hook apis infomation

RECALL_API_INFO g_arHookAPIs[] = 

{

"CreateFileA", "Kernel32.dll", 

MyCreateFileA, CreateFileA,        NULL, 0,

"CreateFileW", "Kernel32.dll", 

MyCreateFileW, CreateFileW, NULL, 0,

"WriteFile", "Kernel32.dll", 

MyWriteFile, WriteFile, NULL, 0,

"WriteFileEx", "Kernel32.dll", 

MyWriteFileEx, WriteFileEx, NULL, 0

}; bool hookApi(PRECALL_API_INFO pApiRecall) // 參數為所劫持的函數資訊

{

if (pApiRecall == NULL)

return false;

// 得到目标函數鎖在的庫名稱

HMODULE hModule = LoadLibrary(pApiRecall->lpDllName);

if (!hModule)

return false;

// 得到目标函數在庫裡面的位置

FARPROC pfnStartAddr = (FARPROC)GetProcAddress(hModule, pApiRecall->lpFunctionName);

pApiRecall->lpApiAddr = pfnStartAddr; // 将目标函數儲存起來,這一步可能不需要,沒試下

if (!pfnStartAddr)

return false;

int nSize = 0; 

int nDisassemblerLen = 0;

while(nSize < 5) 

// GetOpCodeSize can get the assembly code size 得到跳轉指令的大小 詳見GetOpCodeSize的源檔案解釋

nDisassemblerLen = GetOpCodeSize((BYTE*)(pfnStartAddr) + nSize);

PrintMsg("nDisassemblerLen val %d\r\n", nDisassemblerLen);

nSize = nDisassemblerLen + nSize; 

}

PrintMsg("nSize val %d\r\n", nSize);

DWORD dwProtect = 0;

if (!VirtualProtect(pfnStartAddr, nSize, PAGE_EXECUTE_READWRITE, &dwProtect)) // 修改記憶體位址的屬性,将原目标函數位址跳轉指令改為可讀寫模式

return false;

// be sure that we must change pOrgfnMem's protect, because the code in pOrgfnMem 

// also need to execute 

pApiRecall->pOrgfnMem = new BYTE[5 + nSize]; // 這個記憶體區域将儲存原記憶體的跳轉指令

DWORD dwMemProtect = 0;

if (!VirtualProtect(pApiRecall->pOrgfnMem, 5 + nSize, PAGE_EXECUTE_READWRITE, &dwMemProtect))

{

delete [] pApiRecall->pOrgfnMem;

pApiRecall->pOrgfnMem = NULL;

return false;

}

pApiRecall->nOrgfnMemSize = 5 + nSize;

// 下面這幾行就是将原函數的跳轉指令儲存在pOrgfnMem裡面,因為在調用了自定義函數後還要調用原函數,以免影響目标程序的功能

memcpy(pApiRecall->pOrgfnMem, pfnStartAddr, nSize);

*(BYTE*)(pApiRecall->pOrgfnMem + nSize) = 0xE9;

*(DWORD*)(pApiRecall->pOrgfnMem + nSize + 1) = (DWORD)pfnStartAddr + nSize - (DWORD)(pApiRecall->pOrgfnMem + 5 + nSize);

*(BYTE*)(pfnStartAddr) = 0xE9;

*(DWORD*)((BYTE*)pfnStartAddr + 1) = (DWORD)pApiRecall->lpRecallfn - ((DWORD)pfnStartAddr + 5); // lpRecallfn,這是我們自定義的函數,這段語句的作業是用我們自定義的函數覆寫原函數,例如MyWriteFile将會覆寫WriteFile

memset((BYTE*)pfnStartAddr + 5, 0x90, nSize - 5);

// be sure that we must set the rest to 0x90(assembly code for nop, do nothing, 

// and occupy one byte), because we should't change the assembly code

VirtualProtect(pfnStartAddr, nSize, dwProtect, &dwProtect); // 将新函數的跳轉指令的記憶體屬性修改為原來的樣子,可檢視VirtualProtect的作用

return true;

}

第三步、建立注入工程 TestBqDll

執行程序注入操作

操作如下

// 提升程序的權限,不過好像不是必須的

bool promotePrivilege()

{

HANDLE  hToken;

LUID    sedebugnameValue;

TOKEN_PRIVILEGES tkp;

if  ( !OpenProcessToken(  GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)

)

{

return false;

}

if( !LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue) )

{

CloseHandle(hToken);

return false;

}

tkp.PrivilegeCount = 1;

tkp.Privileges[0].Luid = sedebugnameValue;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if( !AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL) )

{

CloseHandle(hToken);

return false;

}

return true;

}

// 根據程序的名稱獲得程序ID,因為程序注入需要程序的ID号

void GetTargetProcessIds(std::string inTarget, std::vector<int > &outIds)

{

PROCESSENTRY32 pe32;

pe32.dwSize = sizeof(pe32);

HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

if(hProcessSnap == INVALID_HANDLE_VALUE)

return;

BOOL bProcess = Process32First(hProcessSnap, &pe32);

int targetNameLen = inTarget.length();

while(bProcess)

{

int searchLen = wcslen(pe32.szExeFile);

if (targetNameLen == searchLen)

{

bool isEqualName = true;

for (int i = 0; i < targetNameLen; ++i)

{

if (inTarget[i] != pe32.szExeFile[i])

{

isEqualName = false;

break;

}

}

if (isEqualName)

outIds.push_back(pe32.th32ProcessID);

}

// 繼續查找

bProcess = Process32Next(hProcessSnap,&pe32);

}

CloseHandle(hProcessSnap);

}

// 程序注入

bool InjectDllByProcessID(const std::wstring dllPath, unsigned long inProcessID)

{

wchar_t* DirPath = new wchar_t[MAX_PATH];

wchar_t* FullPath = new wchar_t[MAX_PATH];

GetCurrentDirectory(MAX_PATH, DirPath);

swprintf_s(FullPath, MAX_PATH, dllPath.c_str(), DirPath);

HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |

PROCESS_VM_WRITE, FALSE, inProcessID);

if (hProcess == NULL)

{

delete[] DirPath;

delete[] FullPath;

return false;

}

LPVOID LoadLibraryAddr = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"),

"LoadLibraryW");

if (LoadLibraryAddr == NULL)

{

CloseHandle(hProcess);

delete[] DirPath;

delete[] FullPath;

return false;

}

LPVOID LLParam = (LPVOID)VirtualAllocEx(hProcess, NULL, (wcslen(FullPath) + 1) * sizeof(wchar_t),

MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

WriteProcessMemory(hProcess, LLParam, FullPath, (wcslen(FullPath) + 1)* sizeof(wchar_t), NULL);

HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryAddr,

LLParam, NULL, NULL);

// 等待遠端線程結束    

::WaitForSingleObject(hRemoteThread, INFINITE);    

// 清理    

::VirtualFreeEx(hProcess, LLParam, (wcslen(FullPath) + 1), MEM_DECOMMIT);    

::CloseHandle(hRemoteThread);    

::CloseHandle(hProcess);

delete[] DirPath;

delete[] FullPath;

return true;

}

void TestBqDll::inject()

{

promotePrivilege();

std::vector<int > processIds;

//GetTargetProcessIds("PosTouch.exe", processIds);

GetTargetProcessIds("TargetProcess.exe", processIds);

QString _workPath = QCoreApplication::applicationDirPath();

QString dllPath = _workPath + "/ApiHook.dll";

if (processIds.size() > 0)

InjectDllByProcessID(dllPath.toStdWString(), processIds[0]);

}

源碼連結http://download.csdn.net/download/sky_calls/10264654

繼續閱讀