现在的位置: 首页 > 综合 > 正文

Pe研究之:从内存中加载Pe文件(代码重定位,进程隐藏,代码注入)

2012年10月08日 ⁄ 综合 ⁄ 共 11102字 ⁄ 字号 评论关闭

 

Pe研究之:
 
从内存中加载Pe文件
 
作者:Sunline              lisunlin0@yahoo.com.cn 日期:2007年7月
关键字:代码重定位,代码注入,远程代码, 加载exe文件, 加载dll文件
Remotthread, injectcode, relocation code, load dll from memory, load pe from memory load pe from memory, execute from meory
 
代码下载:
第二版:(代码几乎完全重写了一遍,修正了很多bug)
概述
在某些特殊场合下需要将Pe文件(.exe, .dll等)直接从内存中启动,比如一段验证软件注册的核心代码, 防Dump的代码, 一些补丁,外挂,当然病毒也常常用这种技术。网上流传了几种解决方案,不过都或多或少存在一些不足,最主要是对MFC程序和带有资源的Pe支持不稳定,或者需要按某些特殊的约定编程,总之不太适合初学者,我这里给出了一个PeBlock的解决方法,可以加载绝大多数的执行文件。
我的这个解决方案克服了前面提到的一些不足,但也没有做到完美。那就是推荐编译时给Pe文件带上重定位信息(一般的Dll是带的,编译自己的exe时需要使用/fixed:no 的编译选项产生重定位表)。更具体的信息请参看示例代码。
这里以库的方式给出LoadPe(…)在自身空间内加载Pe,和LoadPeEx(…)在其它进程空间加载Pe,欢迎大家测试使用。
另外,如果代码不要求加载.exe,仅要求支持Dll,适当修改源代码就可以做到近乎完美了。
具体实现
       Pe文件的加载大体过程是:申请Pe文件建议的内存地址,沿此地址将磁盘中的Pe文件按内存对齐拷贝到内存中,按节属性初始化节,初始化导入函数表,跳入Pe入口执行之。因为网上很早就有这方面知识的相关文章了,这里不再详细叙述,请自己从网上搜索。或参看Bo2000的源代码。
在测试Bo2000的源代码时发现可以使多数的简单.dll文件正确加载,却无法正确加载用MFC生成的.dll文件以及压缩过的可执行文件,仔细调试发现GetModuleFileName是罪魁祸首(另外BO2k里面dll_load存在好几个BUG,这也是其不稳定的直接原因.),调用这个函数将返回NULL,而一般的Dll编写者都默认这个返回结果不会是NULL,并且依赖这个返回结果。
既然找到了原因,那么就可以使用相应的对策来克服之。在这里,我是通过在Pe的映象尾部附加了一个PeBlock的块来解决这个问题(如果仅要求在进程本身加载.dll,这个PeBlock是没有必要的,但是为了能够在其它进程中加载和兼顾加载.exe,使用这个PeBlock是必要的),通过这个PeBlock,不但可以加载.dll,还可以加载.exe(包括压缩的DLL和EXE.)。
注意:这些代码仅作测试,如果要用作非法用途,本人不承担任何法律责任。
其它细节请看打包的源工程文件。
源代码:
// LoadPe.h
// 
// wirite by Sunline 13.July 2007
// 
#pragma once
         // 在内存中加载PE文件(主要支持EXE,DLL以及由之衍生的OCX,AX等)
         // 最原始的代码参见Bo2000中dll_load.cpp。
         // 要求:
         //               推荐带重定位信息,否则对于多数Pe文件是无法成功加载的。
         // 注意:
         //               仅在本人机器上测试,环境:CPU: Celeron 1.7G Memory: 256M OS:WinXp sp2 Complier: VC++6.0 Editor: VS2005
         //               支持Win2000,WinXp,Win2003,WinVista; 
         //               不支持WinMe,Win98,Win95以及更早的Windows产品
         // 限制:
         //               Commandline 应该是"ModuleFileName Param1 param2 ...", 如果ModuleFileName为空,GetModuleFileName将返回空,这对于多数程序是无法接受的。
         //               不支持通过间接途径或通过函数序号调用GetModuleHandle,GetModuleFileName,GetCommandLine且依赖这些函数其返回值的Pe文件。
         //               对于MFC程序,ModuleFileName必须带有"."(参看MFC源代码AppInit.cpp中CWinApp::SetCurrentHandles()函数,要求文件名中必须有"."的存在,比如"my.dll", "my.", ".my"都合法)。
         //               不支持动态链接的MFC程序(原因是在动态链接MFC库的程序中GetModuleHandle,GetModuleFileName,GetCommandLine是在MFC*.dll中调用,违反上述限制)。
         // 如何正确地使用:
         //               fSuicid是一个必须仔细考虑的一个标志:
         //                        设定了fSuicid,且Pe文件是多线程的,则应该保证ExeEntry或DllEntry是最后一个返回的线程,否则会卸载仍有活动线程的内存区域,导致线程访问已卸载的内存空间而出错。
         //                        带apiHook目的的Pe不应该指定fSuicid=TRUE;
 
         // 本人的英语能力有限,大约着用英语作了些注释,呵呵,促进国际化嘛
         // 
         // Load pe file from memory(support exe, dll or other pe file derived from exe/dll, for instances .ocx, .ax eg.)
         // the source file reference dll_load.cpp in Bo2000, you can get more details from it.
         // desire:
         //               desire the pe file with relocation information, or it will failed for most pe files.
         // notice:
         //               It's only test on my machion, Environment: CPU: Celeron 1.7G Memory: 256M OS:WinXp sp2 compile: VC++6.0 Editor: VS2005
         //               surpport Win2000,WinXp,Win2003,WinVista; 
         //               not surpport WinMe,Win98,Win95 or other early Windows os
         //               Commandline contain the modulename, so please assure the Commandline is not NULL
         // Limit:
         //               not surpport the pe file which depend on the GetModuleHandle,GetModuleFileName,GetCommandLine functions but call them indirect.
         //               not suport the pe file which use the MFC in a share dll
         //               CommandLine form as "ModuleFileName Param1 param2 ...", if ModuleFileName is NULL, GetModuleFileName() will return NULL,
         //                        for most pe file, the LoadPeXX() will failed, and 哪位英语好一点的仁兄有时间的话帮忙给把这补全啊, 哈哈, 上面这段英语估计中国人看不懂,外国人也看不懂
         //       
#ifdef __cplusplus
extern "C" {
#endif
        typedef int (__stdcall *PROCUNLOAD)(int uExitCode);
         HINSTANCE __stdcall _LoadLibraryA( LPVOID lpRawRelocPe, LPCSTR lpLibFileName);
         HINSTANCE __stdcall _LoadLibraryW( LPVOID lpRawRelocPe, LPCWSTR lpLibFileName);
         HINSTANCE __stdcall _LoadLibraryExA(LPVOID lpRawRelocPe, LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
         HINSTANCE __stdcall _LoadLibraryExW(LPVOID lpRawRelocPe, LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
         BOOL      __stdcall      _FreeLibrary(   HMODULE hLibModule);
 
         HANDLE __stdcall LoadPeA(LPVOID lpRawRelocPe, LPCSTR lpCommandLine, HINSTANCE *phInst = NULL, BOOL fSuicid = FALSE, PROCUNLOAD *pfUnload = NULL); // if fSuicid is FASLE, the pfUnload should NOT be NULL;
         HANDLE __stdcall LoadPeW(LPVOID lpRawRelocPe, LPCWSTR lpwCommandLine, HINSTANCE *phInst = NULL, BOOL fSuicid = FALSE, PROCUNLOAD *pfUnload = NULL);
         HANDLE __stdcall LoadPeExA(DWORD dwProcessID, LPVOID lpRawRelocPe, LPCSTR lpCommandLine, HINSTANCE *phRemotInst = NULL, BOOL fSuicid = FALSE, PROCUNLOAD *pfUnload = NULL);
         HANDLE __stdcall LoadPeExW(DWORD dwProcessID, LPVOID lpRawRelocPe, LPCWSTR lpwCommandLine, HINSTANCE *phRemotInst = NULL, BOOL fSuicid = FALSE, PROCUNLOAD *pfUnload = NULL);
 
        int __stdcall UnloadPe(PROCUNLOAD pFouncUnload);
        int __stdcall UnloadPeEx(DWORD dwProcessID, PROCUNLOAD pFouncUnload);
         FARPROC __stdcall _GetProcAddress(HMODULE hModule, LPCSTR FuncName);
#ifdef __cplusplus
}
#endif
 
#ifdef UNICODE
         #define LoadPe LoadPeW
         #define LoadPeEx LoadPeExW
#else
         #define LoadPe LoadPeA
         #define LoadPeEx LoadPeExA
#endif
 
 
 
// LoadPe.cpp
#include <windows.h>
#include "libc.h"
#include "RawPeApi.h"
#include "LoadPe.h"
 
extern unsigned char pRawPeBlock[];
char *pzUnLoad = "UnLoad"; // the export function UnLoad name string;
 
// if function succed return the new thread handle, lphInstance point to the memPe instance, pfUnload point to the address to unload the instance
// otherwise return NULL;
HANDLE __stdcall LoadPeA(LPVOID lpRawRelocPe, LPCSTR lpCommandLine, HINSTANCE *phInst, BOOL fSuicid, PROCUNLOAD *pfUnload)
{
         HANDLE hRet = NULL;
        int nCmdLineLen = lstrlenA(lpCommandLine) + 1;
         PWCHAR pwCmdLine = (PWCHAR)_malloc(nCmdLineLen * sizeof(WCHAR));
        if(MultiByteToWideChar(CP_THREAD_ACP, MB_COMPOSITE, lpCommandLine, -1, pwCmdLine, nCmdLineLen))
                 hRet = LoadPeW(lpRawRelocPe, pwCmdLine, phInst, fSuicid, pfUnload);
         _free(pwCmdLine);
        return hRet;
}
 
HANDLE __stdcall LoadPeW(LPVOID lpRawRelocPe, LPCWSTR lpwCommandLine, HINSTANCE *phInst, BOOL fSuicid, PROCUNLOAD *pfUnload)
{
        int nRet = ERROR_UNKNOWN;
        int nPeBlockImageSize = 0;
        int nRawRelocPeImageSize = 0;
        int nCmdLineLenA = 0; // the string length for commandlinea include the end flag.
        int nCmdLineLenW = 0;
        int nMemNead = 0;
         HANDLE hRet = 0;
         LPVOID lpPeBlock = NULL;
         LPVOID lpRelocPe = NULL;
         LPWSTR lpwCmdLine = NULL; // point to the WideByte commandline in lpPeblock
         LPSTR lpCommandLine = NULL; // multibyte commandline;
         LPSTR lpCmdLine = NULL; // point to the multibyte commandline in lpPeblock
         LPPEPARAM pPeParam = NULL;
         MEMORY_BASIC_INFORMATION mbi = {0};
         PIMAGE_DOS_HEADER pDosHeader = NULL;
         PIMAGE_NT_HEADERS pNTHeader = NULL;
         __try
        {
               if(lpwCommandLine == NULL)
                          __leave;
                 nCmdLineLenA = lstrlenW(lpwCommandLine);
                 nCmdLineLenA += sizeof(CHAR);
                 nCmdLineLenW = lstrlenW(lpwCommandLine) * sizeof(WCHAR);
                 nCmdLineLenW += sizeof(WCHAR);
                 lpCommandLine = (LPSTR)_malloc(nCmdLineLenA);
               if(0 == WideCharToMultiByte(CP_THREAD_ACP, 0, lpwCommandLine, -1, lpCommandLine, nCmdLineLenA, NULL, NULL))
                          __leave;
                 nRet = VerifyPE(pRawPeBlock);
               if(nRet)
                          __leave;
                 nRet = VerifyPE(lpRawRelocPe);
               if(nRet)
                          __leave;
 
                 pDosHeader = (PIMAGE_DOS_HEADER)pRawPeBlock;
                 pNTHeader = PIMAGE_NT_HEADERS((LPBYTE)pDosHeader + pDosHeader->e_lfanew);
                 nPeBlockImageSize = pNTHeader->OptionalHeader.SizeOfImage;
               if(pNTHeader->FileHeader.Characteristics & IMAGE_FILE_DLL) // the pRawPeBlock should not be a Dll
               {
                          nRet = ERROR_INVALID_MEMPEBLOCK;
                          __leave;
               }
                 pDosHeader = (PIMAGE_DOS_HEADER)lpRawRelocPe;
                 pNTHeader = PIMAGE_NT_HEADERS((LPBYTE)pDosHeader + pDosHeader->e_lfanew);
                 LPVOID pImageBase = (LPVOID)(ULONG_PTR)pNTHeader->OptionalHeader.ImageBase;
                 nRawRelocPeImageSize = pNTHeader->OptionalHeader.SizeOfImage;
 
                 nMemNead = nRawRelocPeImageSize + nPeBlockImageSize + nCmdLineLenA + nCmdLineLenW + sizeof(PEPARAM);
               if(0 == VirtualQuery(pImageBase, &mbi, sizeof(MEMORY_BASIC_INFORMATION)))
                          __leave;
                 //HANDLE hmapping=CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0, nMemNead,NULL);
                 //if(hmapping==NULL) return NULL;
                 //LPVOID pImgBase=MapViewOfFileEx(hmapping,FILE_MAP_WRITE,0,0,0,pImageBase);
                 //if(pImgBase==NULL) {
                 //       pImgBase=MapViewOfFileEx(hmapping,FILE_MAP_WRITE,0,0,0,NULL);
                 //}
                 //CloseHandle(hmapping); 
                 //if(pImageBase == NULL)
                 //       return 0;
                 //lpRelocPe = pImgBase;
               if((MEM_FREE == mbi.State) && ((SIZE_T)nMemNead < mbi.RegionSize))
               {
                          lpRelocPe = (HINSTANCE)VirtualAlloc(pImageBase, nMemNead, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
               }
               if(lpRelocPe == NULL)
               {
                          lpRelocPe = (HINSTANCE)VirtualAlloc(NULL, nMemNead, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                       if(NULL == lpRelocPe)
                       {
                                   nRet = ERROR_MEMLESS;
                                   __leave;
                       }
               }
                 lpPeBlock = (LPBYTE)lpRelocPe + nRawRelocPeImageSize;
                 lpwCmdLine = LPWSTR((LPBYTE)lpPeBlock + nPeBlockImageSize);
                 lpCmdLine = (LPSTR)((LPBYTE)lpwCmdLine + nCmdLineLenW);
                 pPeParam = (LPPEPARAM)((LPBYTE)lpCmdLine + nCmdLineLenA);
 
                 nRet = AlignPEToMem(lpRawRelocPe, lpRelocPe);
               if(nRet)
                          __leave;
                 nRet = AlignPEToMem(pRawPeBlock, lpPeBlock);
               if(nRet)
                          __leave;
               if(lpRelocPe != pImageBase)
               {
                          nRet = RelocatePe(lpRelocPe, 0, lpRelocPe);
                       if(nRet)
                                   __leave;
               }
 
                 nRet = FillImportTable(lpPeBlock);
               if(nRet)
                          __leave;
                 nRet = RelocatePe(lpPeBlock, 0, lpPeBlock);
               if(nRet)
                          __leave;
                 lstrcpyA(lpCmdLine, lpCommandLine);
                 lstrcpyW(lpwCmdLine, lpwCommandLine);
                 pPeParam->fSuicid = fSuicid;
 
                 pDosHeader = (PIMAGE_DOS_HEADER)pRawPeBlock;
                 pNTHeader = PIMAGE_NT_HEADERS((LPBYTE)pDosHeader + pDosHeader->e_lfanew);
                 FARPROC pEntryPoint = (FARPROC) ((LPBYTE)lpPeBlock + (unsigned int)pNTHeader->OptionalHeader.AddressOfEntryPoint);
                 DWORD dwThreadId;
                 hRet = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pEntryPoint, lpRelocPe, 0, &dwThreadId);
        }
         __except(EXCEPTION_EXECUTE_HANDLER)

抱歉!评论已关闭.