询问者
《Windows图形编程》的调用pe文件类接口时出现写入冲突的错误

问题
-
《Windows图形编程》的pe文件类的实现代码:
#include "StdAfx.h" #include "KPEFile.h" KPEFile::KPEFile(HMODULE hModule) { m_pModule = (LPBYTE)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( LPTSTR pDllName ) { PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)GetDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT); if (pImport==NULL) return NULL; while (pImport->FirstThunk) { LPCSTR szDllName = static_cast<LPCSTR>(RVA2Ptr(pImport->Name)); TCHAR szwszDllName[MAX_PATH]; ::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,szDllName,-1,szwszDllName,MAX_PATH); if (_tcsicmp(pDllName,szwszDllName)==0) { return pImport; } pImport++; } return NULL; } const unsigned long* KPEFile::GetFunctionPtr( PIMAGE_IMPORT_DESCRIPTOR pImport,LPTSTR pProcName ) { PIMAGE_THUNK_DATA32 pThunk; pThunk = (PIMAGE_THUNK_DATA32)RVA2Ptr(pImport->OriginalFirstThunk); // pThunk = (PIMAGE_THUNK_DATA32)RVA2Ptr(pImport->FirstThunk); for (int i = 0;pThunk->u1.Function;i++) { bool match; if (pThunk->u1.Ordinal&IMAGE_ORDINAL_FLAG32) { match = ((pThunk->u1.Ordinal&0xFFFF)==((DWORD)pProcName)); } else { // LPCSTR szFunctionName = static_cast<LPCSTR>(RVA2Ptr((unsigned long)pThunk->u1.AddressOfData)); PIMAGE_IMPORT_BY_NAME pFuncName =static_cast<PIMAGE_IMPORT_BY_NAME>(RVA2Ptr((unsigned long)pThunk->u1.AddressOfData)); TCHAR szwFunctionName[MAX_PATH]; ::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,reinterpret_cast<LPCSTR>(pFuncName->Name),-1,szwFunctionName,MAX_PATH); match = ((_tcsicmp(pProcName,szwFunctionName))==0); } if (match) return (unsigned long*)RVA2Ptr(pImport->FirstThunk)+i; pThunk++; } return NULL; } FARPROC KPEFile::SetImportAddress( LPTSTR pDllName,LPTSTR pProcName,FARPROC pNewProc ) { PIMAGE_IMPORT_DESCRIPTOR pImport = GetImportDescriptor(pDllName); if (NULL!=pImport) { const unsigned long* pfn = GetFunctionPtr(pImport,pProcName); if(::IsBadReadPtr(pfn,sizeof(DWORD))) return NULL; // 读取原来的地址 FARPROC oldproc = (FARPROC)*pfn; DWORD dwWritten; BOOL bRet = WriteProcessMemory(GetCurrentProcess(),(void*)pfn,&pNewProc,sizeof(DWORD),&dwWritten); return oldproc; } else return NULL; } FARPROC KPEFile::SetExportAddress( LPTSTR pProcName,FARPROC pNewProc ) { PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)GetDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT); if (NULL==pExport) return NULL; unsigned ord =0; if ((unsigned)pProcName<0xFFFF) //ordinal { ord = (unsigned)pProcName; } else { const DWORD* pNames = (const DWORD*)RVA2Ptr(pExport->AddressOfNames); const WORD *pOrds = (const WORD*)RVA2Ptr(pExport->AddressOfNameOrdinals); for (unsigned i =0;i<pExport->AddressOfNames;i++) { if(_tcsicmp(pProcName,static_cast<TCHAR*>(RVA2Ptr(pNames[i])))==0) { ord = pExport->Base + pOrds[i]; break; } } } if ((ord<pExport->Base)||(ord>pExport->NumberOfFunctions)) return NULL; DWORD dwWritten = 0; DWORD *pRVA = (DWORD*)RVA2Ptr(pExport->AddressOfFunctions)+ord-pExport->Base; DWORD rslt = *pRVA; DWORD newRVA = (DWORD)pNewProc-(DWORD)m_pModule; WriteProcessMemory(GetCurrentProcess(),pRVA,&newRVA,sizeof(DWORD),&dwWritten); return (FARPROC)RVA2Ptr(rslt); }
外部调用这个类的接口SetImportAddress的代码:
int WINAPI MyMessageBox(HWND hWnd,LPCTSTR pText,LPCTSTR pCaption,UINT nType) { TCHAR szText[MAX_PATH]; TCHAR szCaption[MAX_PATH]; _tcscat(szText,_T("intercepted")); _tcscat(szCaption,_T("intercepted")); return ::MessageBox(hWnd,pText,pCaption,nType); } int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 KPEFile pe(hInstance); pe.SetImportAddress(_T("user32.dll"),_T("MessageBoxW"),(FARPROC)MyMessageBox); ::MessageBox(NULL,_T("Test"),_T("SetImportaddress"),MB_OK); return 0; }
这个程序要实现的功能是将该进程导入的user32.dll中的MessageBoxW函数动态替换为我编写的MyMessageBox,程序编译环境为 VS C++2005,unicode字符集。经过调试进入 KPEFile::SetImportAddress后能找到MessageBoxW函数的地址,WriteProcessMemory函数的返回值也是正确的,但是运行完::MessageBox(NULL,_T("Test"),_T("SetImportaddress"),MB_OK);这一步后却出现写入冲突,错误图片如下:
然后打开调用堆栈,没有看到有用的信息。
前无古人,后无来者