windows下在内存中运行程序

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;   
}

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;   
}  

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;   
}

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;   
}

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");
    }
}

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

你可能感兴趣的:(windows)