windows内存加载程序
本文地址:http://tongxinmao.com/Article/Detail/id/334
1.前言
公司需要将两个程序合并成一个,一个我们自己的,一个客户的,运行的时候先运行我们的再运行客户的,否则无法进入游戏。现在有一个问题就是说部分客户需要在哪里打开在哪里运行,这样如果采用和原来一样的释放打开的方式,就可能会出现玩家放在桌面上的时候直接点击客户的,导致无法正常进入。故这里打算采用一种黑技术来实现,我运行的时候将客户的程序,直接运行在内存中,而不是将其释放出来。
2.解决
大致思路:我运行一个傀儡程序,然后将其挂起,用新程序填充原程序空间,然后运行即可。
2.1 运行傀儡程序
// 创建外壳进程并获取其基址、大小和当前运行状态 BOOL CreateChild(char *Cmd, CONTEXT &Ctx, HANDLE &ProcHnd, HANDLE &ThrdHnd, unsigned long &ProcId, unsigned long &BaseAddr, unsigned long &ImageSize) { STARTUPINFOA si; PROCESS_INFORMATION pi; unsigned long old; MEMORY_BASIC_INFORMATION MemInfo; memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); si.cb = sizeof(si); BOOL res = CreateProcessA(NULL, Cmd, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); // 以挂起方式运行进程; if(res){ ProcHnd = pi.hProcess; ThrdHnd = pi.hThread; ProcId = pi.dwProcessId; // 获取外壳进程运行状态,[ctx.Ebx+8]内存处存的是外壳进程的加载基址,ctx.Eax存放有外壳进程的入口地址 Ctx.ContextFlags = CONTEXT_FULL; GetThreadContext(ThrdHnd, &Ctx); ReadProcessMemory(ProcHnd, (void *)(Ctx.Ebx+8), &BaseAddr, sizeof(unsigned long), &old); // 读取加载基址 void *p = (void *)BaseAddr; // 计算外壳进程占有的内存 while(VirtualQueryEx(ProcHnd, p, &MemInfo, sizeof(MemInfo))) { if(MemInfo.State = MEM_FREE) break; p = (void *)((unsigned long)p + MemInfo.RegionSize); } ImageSize = (unsigned long)p - (unsigned long)BaseAddr; } return res; }1234567891011121314151617181920212223242526272829303132
2.2 获取新程序的信息
// 加载pe到内存并对齐所有节 BOOL AlignPEToMem( void *Buf , long Len , PIMAGE_NT_HEADERS &peH , PIMAGE_SECTION_HEADERS &peSecH , void *&Mem , unsigned long &ImageSize) { PIMAGE_DOS_HEADER SrcMz;// DOS头 PIMAGE_NT_HEADERS SrcPeH;// PE头 PIMAGE_SECTION_HEADERS SrcPeSecH;// 节表 SrcMz = (PIMAGE_DOS_HEADER)Buf; if( Len < sizeof(IMAGE_DOS_HEADER) ) return FALSE; if( SrcMz->e_magic != IMAGE_DOS_SIGNATURE ) return FALSE; if( Len < SrcMz->e_lfanew + (long)sizeof(IMAGE_NT_HEADERS) ) return FALSE; SrcPeH = (PIMAGE_NT_HEADERS)((int)SrcMz + SrcMz->e_lfanew); if( SrcPeH->Signature != IMAGE_NT_SIGNATURE ) return FALSE; if( (SrcPeH->FileHeader.Characteristics & IMAGE_FILE_DLL) || (SrcPeH->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE == 0) || (SrcPeH->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER)) ) { return FALSE; } SrcPeSecH = (PIMAGE_SECTION_HEADERS)((int)SrcPeH + sizeof(IMAGE_NT_HEADERS)); ImageSize = CalcTotalImageSize( SrcMz, Len, SrcPeH, SrcPeSecH); if( ImageSize == 0 ) return FALSE; Mem = VirtualAlloc( NULL, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配内存 if( Mem != NULL ) { // 计算需要复制的PE头字节数 unsigned long l = SrcPeH->OptionalHeader.SizeOfHeaders; for( int i = 0; i < SrcPeH->FileHeader.NumberOfSections; ++i) { if( (SrcPeSecH[i]->PointerToRawData) && (SrcPeSecH[i]->PointerToRawData < l) ) { l = SrcPeSecH[i]->PointerToRawData; } } memmove( Mem, SrcMz, l); peH = (PIMAGE_NT_HEADERS)((int)Mem + ((PIMAGE_DOS_HEADER)Mem)->e_lfanew); peSecH = (PIMAGE_SECTION_HEADERS)((int)peH + sizeof(IMAGE_NT_HEADERS)); void *Pt = (void *)((unsigned long)Mem + GetAlignedSize( peH->OptionalHeader.SizeOfHeaders , peH->OptionalHeader.SectionAlignment) ); for( int i = 0; i < peH->FileHeader.NumberOfSections; ++i) { // 定位该节在内存中的位置 if(peSecH[i]->VirtualAddress) Pt = (void *)((unsigned long)Mem + peSecH[i]->VirtualAddress); if(peSecH[i]->SizeOfRawData) { // 复制数据到内存 memmove(Pt, (const void *)((unsigned long)(SrcMz) + peSecH[i]->PointerToRawData), peSecH[i]->SizeOfRawData); if(peSecH[i]->Misc.VirtualSize < peSecH[i]->SizeOfRawData) Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->SizeOfRawData, peH->OptionalHeader.SectionAlignment)); else // pt 定位到下一节开始位置 Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment)); } else { Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment)); } } } return TRUE; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
2.3 卸载原来的填入新的并运行
// 创建外壳进程并用目标进程替换它然后执行 HANDLE AttachPE(char *CmdParam, PIMAGE_NT_HEADERS peH, PIMAGE_SECTION_HEADERS peSecH, void *Ptr, unsigned long ImageSize, unsigned long &ProcId) { HANDLE res = INVALID_HANDLE_VALUE; CONTEXT Ctx; HANDLE Thrd; unsigned long Addr, Size; char *s = PrepareShellExe(CmdParam, peH->OptionalHeader.ImageBase, ImageSize); if(s==NULL) return res; if(CreateChild(s, Ctx, res, Thrd, ProcId, Addr, Size)){ void *p = NULL; unsigned long old; if((peH->OptionalHeader.ImageBase == Addr) && (Size >= ImageSize)){// 外壳进程可以容纳目标进程并且加载地址一致 p = (void *)Addr; VirtualProtectEx(res, p, Size, PAGE_EXECUTE_READWRITE, &old); } else if(IsNT()){ if(UnloadShell(res, Addr)){// 卸载外壳进程占有内存 p = MyVirtualAllocEx((unsigned long)res, (void *)peH->OptionalHeader.ImageBase, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); } if((p == NULL) && HasRelocationTable(peH)){// 分配内存失败并且目标进程支持重定向 p = MyVirtualAllocEx((unsigned long)res, NULL, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(p) DoRelocation(peH, Ptr, p); // 重定向 } } if(p){ WriteProcessMemory(res, (void *)(Ctx.Ebx+8), &p, sizeof(DWORD), &old); // 重置目标进程运行环境中的基址 peH->OptionalHeader.ImageBase = (unsigned long)p; if(WriteProcessMemory(res, p, Ptr, ImageSize, &old)){// 复制PE数据到目标进程 Ctx.ContextFlags = CONTEXT_FULL; if((unsigned long)p == Addr) Ctx.Eax = peH->OptionalHeader.ImageBase + peH->OptionalHeader.AddressOfEntryPoint; // 重置运行环境中的入口地址 else Ctx.Eax = (unsigned long)p + peH->OptionalHeader.AddressOfEntryPoint; SetThreadContext(Thrd, &Ctx);// 更新运行环境 ResumeThread(Thrd);// 执行 CloseHandle(Thrd); } else{// 加载失败,杀掉外壳进程 TerminateProcess(res, 0); CloseHandle(Thrd); CloseHandle(res); res = INVALID_HANDLE_VALUE; } } else{// 加载失败,杀掉外壳进程 TerminateProcess(res, 0); CloseHandle(Thrd); CloseHandle(res); res = INVALID_HANDLE_VALUE; } } delete[] s; return res; }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
2.4 资源中加载到内存
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HRSRC hrSrc; HGLOBAL hGlobal; LPVOID lpExe; DWORD dwSize; HANDLE hFile; unsigned long ulProcessId = 0; hrSrc = FindResource(NULL, TEXT("CHECK"), TEXT("BIN")); hGlobal = LoadResource(NULL, hrSrc); lpExe = LockResource(hGlobal); dwSize = SizeofResource(NULL, hrSrc); MemExecute(lpExe, dwSize, "", &ulProcessId); UnlockResource(hGlobal); FreeResource(hGlobal); getchar(); return 0; }1234567891011121314151617181920212223
2.5 外部导入程序到资源
void loadexe() { HANDLE hUpdateRes; hUpdateRes = BeginUpdateResource(strDesPath.c_str(), FALSE); if (hUpdateRes != NULL) { HANDLE hOpenFile = (HANDLE)CreateFile(wstrloginpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (hOpenFile != INVALID_HANDLE_VALUE) { DWORD fileSize = GetFileSize(hOpenFile, NULL); char *pBuffer = (char *) malloc(fileSize + 1); //这里可能会失败 DWORD RSize = 0; ReadFile(hOpenFile, pBuffer, fileSize, &RSize, NULL); int result = UpdateResource(hUpdateRes, TEXT("FILE"), TEXT("ALACLIENT"), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (char *)pBuffer, fileSize); //fileSize + 5 if (!result) writelog("failed UpdateResource exe in generatefunc"); CloseHandle(hOpenFile); free(pBuffer); } if (!EndUpdateResource(hUpdateRes, FALSE)) writelog("failed EndUpdateResource in generatefunc"); } }12345678910111213141516171819202122232425262728293031
3.备注
3.1 在win7+vs2010 下运行正常 需要对项目属性设置下 右键属性–链接器–高级–随机基址:否 固定基址:是
3.2 完整demo下载地址:http://download.csdn.net/detail/zhang_ruiqiang/9608366
3.3 文章1: http://blog.csdn.net/ooyyee/article/details/51842075
3.3 文章2: http://blog.csdn.net/aroc_lo/article/details/5448700
上一篇:各种螺丝代号及图片
下一篇:Linux下C语言来检测USB设备以及自动区分U盘和硬盘并自动挂载