直接运行内存中的程序

本文详细介绍了如何使用C++实现PE文件加载至内存并进行内存对齐的技术,包括计算对齐后的大小、PE头及各节的大小、以及内存分配与数据复制的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include "stdafx.h"  
#include <windows.h>
  
typedef IMAGE_SECTION_HEADER (*PIMAGE_SECTION_HEADERS)[1];   
  
// 计算对齐后的大小   
unsigned long GetAlignedSize(unsigned long Origin, unsigned long Alignment)   
  
    return (Origin Alignment 1) Alignment Alignment;   
  
  
// 计算加载pe并对齐需要占用多少内存   
// 未直接使用OptionalHeader.SizeOfImage作为结果是因为据说有的编译器生成的exe这个值会填0   
unsigned long CalcTotalImageSize(PIMAGE_DOS_HEADER MzH   
                                 unsigned long FileLen   
                                 PIMAGE_NT_HEADERS peH   
                                 PIMAGE_SECTION_HEADERS peSecH)   
  
    unsigned long res;   
    // 计算pe头的大小   
    res GetAlignedSize( peH->OptionalHeader.SizeOfHeaders   
        peH->OptionalHeader.SectionAlignment   
        );   
  
    // 计算所有节的大小   
    for( int 0; peH->FileHeader.NumberOfSections; ++i)   
      
        // 超出文件范围   
        if(peSecH[i]->PointerToRawData peSecH[i]->SizeOfRawData FileLen)   
            return 0;   
        else if(peSecH[i]->VirtualAddress)//计算对齐后某节的大小   
          
            if(peSecH[i]->Misc.VirtualSize)   
              
                res GetAlignedSize( peSecH[i]->VirtualAddress peSecH[i]->Misc.VirtualSize   
                    peH->OptionalHeader.SectionAlignment   
                    );   
              
            else  
              
                res GetAlignedSize( peSecH[i]->VirtualAddress peSecH[i]->SizeOfRawData   
                    peH->OptionalHeader.SectionAlignment   
                    );   
              
          
        else if( peSecH[i]->Misc.VirtualSize peSecH[i]->SizeOfRawData   
          
            res += GetAlignedSize( peSecH[i]->SizeOfRawData   
                peH->OptionalHeader.SectionAlignment   
                );   
          
        else  
          
            res += GetAlignedSize( peSecH[i]->Misc.VirtualSize   
                peH->OptionalHeader.SectionAlignment   
                );   
        }// if_else   
    }// for   
       
    return res;   
  
  
  
  
  
// 加载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 ==   
        return FALSE;   
       
    Mem VirtualAlloc( NULL, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配内存   
    if( Mem != NULL   
      
        // 计算需要复制的PE头字节数   
        unsigned long SrcPeH->OptionalHeader.SizeOfHeaders;   
        for( int 0; SrcPeH->FileHeader.NumberOfSections; ++i)   
          
            if( (SrcPeSecH[i]->PointerToRawData) &&   
                (SrcPeSecH[i]->PointerToRawData 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( 0; 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;   
  
  
  
  
typedef void *(__stdcall *pfVirtualAllocEx)(unsigned long, void *, unsigned long, unsigned long, unsigned long);   
pfVirtualAllocEx MyVirtualAllocEx NULL;   
  
BOOL IsNT()   
  
    return MyVirtualAllocEx!=NULL;   
  
  
// 生成外壳程序命令行   
char *PrepareShellExe(char *CmdParam, unsigned long BaseAddr, unsigned long ImageSize)   
  
    if(IsNT())   
      
        char *Buf new char[256];   
        memset(Buf, 0, 256);   
        GetModuleFileName(0, Buf, 256);   
        strcat(Buf, CmdParam);   
        return Buf; // 请记得释放内存;-)   
      
    else  
      
        // Win98下的处理请参考原文;-)   
        // http://community.youkuaiyun.com/Expert/topic/4416/4416252.xml?temp=8.709133E-03   
        return NULL;   
      
  
  
// 是否包含可重定向列表   
BOOL HasRelocationTable(PIMAGE_NT_HEADERS peH)   
  
    return (peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)   
        && (peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);   
  
  
  
  
  
#pragma pack(push, 1)   
typedef struct{   
    unsigned long VirtualAddress;   
    unsigned long SizeOfBlock;   
*PImageBaseRelocation;   
#pragma pack(pop)   
  
// 重定向PE用到的地址   
void DoRelocation(PIMAGE_NT_HEADERS peH, void *OldBase, void *NewBase)   
  
    unsigned long Delta (unsigned long)NewBase peH->OptionalHeader.ImageBase;   
    PImageBaseRelocation (PImageBaseRelocation)((unsigned long)OldBase    
        peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);   
    while(p->VirtualAddress p->SizeOfBlock)   
      
        unsigned short *pw (unsigned short *)((int)p sizeof(*p));   
        for(unsigned int i=1; <= (p->SizeOfBlock sizeof(*p)) 2; ++i)   
          
            if((*pw) 0xF000 == 0x3000){   
                unsigned long *t (unsigned long *)((unsigned long)(OldBase) p->VirtualAddress ((*pw) 0x0FFF));   
                *t += Delta;   
              
            ++pw;   
          
        (PImageBaseRelocation)pw;   
      
  
  
// 卸载原外壳占用内存   
BOOL UnloadShell(HANDLE ProcHnd, unsigned long BaseAddr)   
  
    typedef unsigned long (__stdcall *pfZwUnmapViewOfSection)(unsigned long, unsigned long);   
    pfZwUnmapViewOfSection ZwUnmapViewOfSection NULL;   
    BOOL res FALSE;   
    HMODULE LoadLibrary("ntdll.dll");   
    if(m){   
        ZwUnmapViewOfSection (pfZwUnmapViewOfSection)GetProcAddress(m, "ZwUnmapViewOfSection");   
        if(ZwUnmapViewOfSection)   
            res (ZwUnmapViewOfSection((unsigned long)ProcHnd, BaseAddr) == 0);   
        FreeLibrary(m);   
      
    return res;   
  
  
// 创建外壳进程并获取其基址、大小和当前运行状态   
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 CreateProcess(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;   
            (void *)((unsigned long)p MemInfo.RegionSize);   
          
        ImageSize (unsigned long)p (unsigned long)BaseAddr;   
      
    return res;   
  
  
// 创建外壳进程并用目标进程替换它然后执行   
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)){// 外壳进程可以容纳目标进程并且加载地址一致   
            (void *)Addr;   
            VirtualProtectEx(res, p, Size, PAGE_EXECUTE_READWRITE, &old);   
          
        else if(IsNT()){   
            if(UnloadShell(res, Addr)){// 卸载外壳进程占有内存   
                MyVirtualAllocEx((unsigned long)res, (void *)peH->OptionalHeader.ImageBase, ImageSize, MEM_RESERVE MEM_COMMIT, PAGE_EXECUTE_READWRITE);   
              
            if((p == NULL) && HasRelocationTable(peH)){// 分配内存失败并且目标进程支持重定向   
                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;   
  
  
  
  
  
   
HANDLE MemExecute(void *ABuffer, long Len, char *CmdParam, unsigned long *ProcessId)   
  
    HANDLE res INVALID_HANDLE_VALUE;   
    PIMAGE_NT_HEADERS peH;   
    PIMAGE_SECTION_HEADERS peSecH;   
    void *Ptr;   
    unsigned long peSz;   
    if(AlignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz))   
      
        res AttachPE(CmdParam, peH, peSecH, Ptr, peSz, *ProcessId);   
        VirtualFree(Ptr, peSz, MEM_DECOMMIT);   
      
    return res;   
  
  
// 初始化   
class CInit   
  
public:   
    CInit()   
      
        MyVirtualAllocEx (pfVirtualAllocEx)GetProcAddress(GetModuleHandle("Kernel32.dll"), "VirtualAllocEx");   
      
}Init;   
  
  
  
  
int main(int argc, char **argv)
  
    HANDLE hFile NULL;   
    hFile ::CreateFile( "C:\\Windows\\System32\\cmd.exe"  
        FILE_ALL_ACCESS   
          
        NULL   
        OPEN_EXISTING   
        FILE_ATTRIBUTE_NORMAL   
        NULL   
        );   
    if( hFile == INVALID_HANDLE_VALUE   
        return -1;   
  
    ::SetFilePointer( hFile, 0, NULL, FILE_BEGIN);   
    DWORD dwFileSize ::GetFileSize( hFile, NULL);   
  
    LPBYTE pBuf new BYTE[dwFileSize];   
    memset( pBuf, 0, dwFileSize);   
  
    DWORD dwNumberOfBytesRead 0;   
    ::ReadFile( hFile   
        pBuf   
        dwFileSize   
        &dwNumberOfBytesRead   
        NULL   
        );   
  
    ::CloseHandle(hFile);   
       
    unsigned long ulProcessId 0;   
    MemExecute( pBuf, dwFileSize, "", &ulProcessId);   
    delete[] pBuf;   
  
       
    return 0;   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值