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盘和硬盘并自动挂载