windows内存加载程序
本文地址:http://txm.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;
}12345678910111213141516171819202122232425262728293031322.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;
} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384852.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;
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455562.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;
}12345678910111213141516171819202122232.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");
}
}123456789101112131415161718192021222324252627282930313.备注
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盘和硬盘并自动挂载