最近在看《windows圖形編程》,其中提到一些比較實用的windows系統的檔案特性,總結一下,把代碼也貼出來作參考。
原文參考P27,在這裡只把代碼寫出來,代碼經過我的初步修改,可以直接運行。
要求要瞭解PE檔案結構,熟悉基本的windowsAPI。下面程式把原來需要調用user32.dll中的MessageBoxA函數用自己實現的MyMessageBox代替,也就是一個重新導向的功能。
// NormalProject.cpp : Defines the entry point for the application.//#include "stdafx.h"#include <windows.h>#include "NormalProject.h"#include "KPEFile.h"int WINAPI MyMessageA(HWND hWnd, LPCSTR pText, LPCSTR pCaption, UINT uType);int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){KPEFile pe(hInstance);pe.SetImportAddress("user32.dll","MessageBoxA",(FARPROC)MyMessageA);MessageBoxA(NULL,"Test","SetImportAddress",MB_OK);}int WINAPI MyMessageA(HWND hWnd, LPCSTR pText, LPCSTR pCaption, UINT uType){WCHAR wText[MAX_PATH];WCHAR wCaption[MAX_PATH];MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pText,-1,wText,MAX_PATH);wcscat(wText,L"-intercepted");MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,pCaption,-1,wCaption,MAX_PATH);wcscat(wCaption,L"-intercepted");return MessageBoxW(hWnd,wText,wCaption,uType);}
接下來是KPEFile.h
#pragma onceclass KPEFile{const char * m_pModule;PIMAGE_DOS_HEADER m_pDOSHeader;PIMAGE_NT_HEADERS m_pNTHeader;public:KPEFile(HMODULE hModule);~KPEFile(void);const char * RVA2Ptr(unsigned rva) {if ((m_pModule!=NULL)&&rva){return m_pModule+rva;}else{return NULL;}}const void * GetDirectory(int id);PIMAGE_IMPORT_DESCRIPTOR GetImportDescriptor(LPCSTR pDllName);const unsigned * GetFunctionPtr(PIMAGE_IMPORT_DESCRIPTOR pImport,LPCSTR pProcName);FARPROC SetImportAddress(LPCSTR pDllName, LPCSTR pProcName, FARPROC pNewProc);};
以及實現代碼:
#include "StdAfx.h"#include "KPEFile.h"KPEFile::KPEFile(HMODULE hModule){m_pModule = (const char *)hModule;if (IsBadReadPtr(m_pModule, sizeof(IMAGE_DOS_HEADER))){m_pDOSHeader = NULL;m_pNTHeader = NULL;}else{m_pDOSHeader = (PIMAGE_DOS_HEADER)m_pModule;if (IsBadReadPtr(RVA2Ptr(m_pDOSHeader->e_lfanew),sizeof(IMAGE_NT_HEADERS))){m_pNTHeader = NULL;}else{m_pNTHeader = (PIMAGE_NT_HEADERS)RVA2Ptr(m_pDOSHeader->e_lfanew);}}}KPEFile::~KPEFile(void){}const void * KPEFile::GetDirectory(int id){return RVA2Ptr(m_pNTHeader->OptionalHeader.DataDirectory[id].VirtualAddress);}PIMAGE_IMPORT_DESCRIPTOR KPEFile::GetImportDescriptor(LPCSTR pDllName){PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)GetDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT);if ( pImport == NULL){return NULL;}while(pImport->FirstThunk){if (stricmp(pDllName,RVA2Ptr(pImport->Name))==0){return pImport;}pImport++;}return NULL;}const unsigned * KPEFile::GetFunctionPtr(PIMAGE_IMPORT_DESCRIPTOR pImport, LPCSTR pProcName){PIMAGE_THUNK_DATA pThunk;pThunk = (PIMAGE_THUNK_DATA)RVA2Ptr(pImport->OriginalFirstThunk);for (int i =0; pThunk->u1.Function; i++){bool match;if (pThunk->u1.Ordinal & 0x80000000){match = (pThunk->u1.Ordinal&0xFFFF) == ((DWORD)pProcName);}else{match = stricmp(pProcName,RVA2Ptr((unsigned)pThunk->u1.AddressOfData)+2) == 0;}if (match){return (unsigned*)RVA2Ptr(pImport->FirstThunk)+i;}pThunk++;}return NULL;}FARPROC KPEFile::SetImportAddress(LPCSTR pDllName, LPCSTR pProcName, FARPROC pNewProc){PIMAGE_IMPORT_DESCRIPTOR pImport = GetImportDescriptor(pDllName);if (pImport){const unsigned * pfn = GetFunctionPtr(pImport,pProcName);if (IsBadReadPtr(pfn, sizeof(DWORD))){return NULL;}FARPROC oldproc = (FARPROC) * pfn;DWORD dwWritten;WriteProcessMemory(GetCurrentProcess(),(void *)pfn,&pNewProc, sizeof(DWORD), &dwWritten); //改變符號指向的地址,實現鉤子目的。return oldproc;}else{return NULL;}}
沒有太多時間,代碼中沒有一一注釋。
在一些木馬以及防木馬的技術中,這個技術應該是可以應用的。