运行 PE 文件 - (可执行)从内存

逆向工程 C++ 聚乙烯 x64 视窗 10
2021-06-20 17:57:05

非常感谢您的建议。尝试在(Windows 10-64位/Visual Studio)程序上运行 32/64 位,该程序试图从内存中运行可执行文件。这是为了我的学习目的

#include <iostream> // Standard C++ library for console I/O
#include <string> // Standard C++ Library for string manip
#include <Windows.h> // WinAPI Header
#include <TlHelp32.h> //WinAPI Process API

#ifdef _WIN64
typedef IMAGE_NT_HEADERS64  IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
#else
typedef IMAGE_NT_HEADERS32  IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
#endif 


// use this if you want to read the executable from disk
HANDLE MapFileToMemory(LPCSTR filename)
{
  std::streampos size;
  std::fstream file(filename, std::ios::in | std::ios::binary 
 |std::ios::ate);
 if (file.is_open())
 {
    size = file.tellg();

    char* Memblock = new char[size]();

    file.seekg(0, std::ios::beg);
    file.read(Memblock, size);
    file.close();

    return Memblock;
    }
 return 0;
 }
 int RunPortableExecutable(void* Image)
  {
   IMAGE_DOS_HEADER* DOSHeader; // For Nt DOS Header symbols
   IMAGE_NT_HEADERS* NtHeader; // For Nt PE Header objects & symbols
   IMAGE_SECTION_HEADER* SectionHeader;

    PROCESS_INFORMATION PI; 
    STARTUPINFOA SI; 
    CONTEXT* CTX;


DWORD* ImageBase; //Base address of the image
void* pImageBase; // Pointer to the image base

int count;
char CurrentFilePath[1024];

DOSHeader = PIMAGE_DOS_HEADER(Image); // Initialize Variable
NtHeader = PIMAGE_NT_HEADERS(DWORD(Image) + DOSHeader->e_lfanew); // Initialize

GetModuleFileNameA(0, CurrentFilePath, 1024); // path to current executable

if (NtHeader->Signature == IMAGE_NT_SIGNATURE) // Check if image is a PE File.
{
    ZeroMemory(&PI, sizeof(PI)); // Null the memory
    ZeroMemory(&SI, sizeof(SI)); // Null the memory

    if (CreateProcessA(CurrentFilePath, NULL, NULL, NULL, FALSE,
        CREATE_SUSPENDED, NULL, NULL, &SI, &PI)) // Create a new instance of current
        //process in suspended state, for the new image.
    {
        // Allocate memory for the context.
        CTX = LPCONTEXT(VirtualAlloc(NULL, sizeof(CTX), MEM_COMMIT, PAGE_READWRITE));
        CTX->ContextFlags = CONTEXT_FULL; // Context is allocated

        if (GetThreadContext(PI.hThread, LPCONTEXT(CTX))) //if context is in thread
        {
            // Read instructions
            ReadProcessMemory(PI.hProcess, LPCVOID(CTX->Rbx + 8), LPVOID(&ImageBase), 4, 0);

            pImageBase = VirtualAllocEx(PI.hProcess, LPVOID(NtHeader->OptionalHeader.ImageBase),
                NtHeader->OptionalHeader.SizeOfImage, 0x3000, PAGE_EXECUTE_READWRITE);

            // Write the image to the process
            WriteProcessMemory(PI.hProcess, pImageBase, Image, NtHeader->OptionalHeader.SizeOfHeaders, NULL);

            for (count = 0; count < NtHeader->FileHeader.NumberOfSections; count++)
            {
                SectionHeader = PIMAGE_SECTION_HEADER(DWORD(Image) + DOSHeader->e_lfanew + 248 + (count * 40));

                WriteProcessMemory(PI.hProcess, LPVOID(DWORD(pImageBase) + SectionHeader->VirtualAddress),
                    LPVOID(DWORD(Image) + SectionHeader->PointerToRawData), SectionHeader->SizeOfRawData, 0);
            }
            WriteProcessMemory(PI.hProcess, LPVOID(CTX->Rbx + 8),
                LPVOID(&NtHeader->OptionalHeader.ImageBase), 4, 0);

            // Move address of entry point to the eax register
            CTX->Rax = DWORD(pImageBase) + NtHeader->OptionalHeader.AddressOfEntryPoint;
            SetThreadContext(PI.hThread, LPCONTEXT(CTX)); // Set the context
            ResumeThread(PI.hThread); //´Start the process/call main()

            return 0; // Operation was successful.
        }
    }
}

      //enter valid bytes of a  *64 BIT* program here.
      //Sample code 
       unsigned char rawData[37376] = {
       0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00}; 

    int main()
 {
      RunPortableExecutable(rawData); // run executable from the array - 
                                 // 64bit program in hex -coverted from exe
       getchar();
 }

在 x64 平台上的 VS 2017 上成功编译后,出现异常。

 if(NtHeader->Signature == IMAGE_NT_SIGNATURE) // Check if image is a PE File  

仍然对 64 位 PE 格式如何在 Windows 10 上工作一无所知。是运行代码(十六进制格式)的问题还是该程序本身的问题?我缺少任何积分吗?任何有用的建议。

4个回答

我设法调试并找到了您的问题,但它确实超出了范围,因为您的“64 位”程序的“字节”没有“ IMAGE_NT_SIGNATURE ”(PE00)而是“ IMAGE_DOS_SIGNATURE ”(MZ)。https://i.imgur.com/vPn8EIK.png

如果我是你,我会首先找到/准备正确的代码来运行它作为 SHELL CODE(顺便说一下,你知道漏洞利用/shells/恶意软件使用相同的方法是吗?)。

此外,从“此处64 位程序的有效字节”到结尾,您的方法都是错误 的。

我不会发布完整的解决方案/答案,因为那样你将一无所获。除了我所说的之外,使用以下代码段,您将成功实现“学习目的”:

IMAGE_DOS_HEADER* lpDosHeader = (IMAGE_DOS_HEADER*)lpBaseAddress;

if(lpDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
    return 0;
}

IMAGE_NT_HEADERS* lpNtHeader = (IMAGE_NT_HEADERS*)(lpBaseAddress+lpDosHeader->e_lfanew);

if(lpNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
    return 0;
}

注意“ lpBaseAddress ”是BYTE*

更多参考:https : //stackoverflow.com/questions/38161809/generate-shellcode-from-an-exe

所以,你已经初始化了几个字节:

unsigned char rawData[37376] = { 0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00};

但是,您正在访问未在 rawData 中初始化的 DOSHeader->e_lfanew。最有可能的数据 DOSHeader->e_lfanew 包含一些垃圾,如 0xCCCCCCCC 或 0xDDDDDDDD,因此 NtHeader 指向 rawData 之外的一些内存,并且您在尝试访问不存在的内存地址时遇到异常。

规范示例 (IMHO) 是由 Joachim Bauch 在他的MemoryModule项目中用 C 编写的

有一个名为BTMemoryModule的 Object-Pascal 端口(Lazarus 和 Delphi)

对于将来看到这一点的任何人。问题确实是提问者认为的 64 位与 32 位问题。错误就在这一行NtHeader = PIMAGE_NT_HEADERS(DWORD(Image) + DOSHeader->e_lfanew);

在这里,您尝试添加两个指针以获取地址并将其转换为指向IMAGE_NT_HEADERS结构的指针64 位指针,正如@dee_two 提到的那样是 64 位长,并且DWORD-ing 它们会截断它们;你应该从 vs 编译器那里得到关于这个的警告。

解决方案在这里:永远不要对 DWORD 的指针进行类型转换,尤其是在 64 位体系结构上。而是将其类型转换为 ULONG_PTR 或 UINT_PTR,如下所示: NtHeader = PIMAGE_NT_HEADERS(ULONG_PTR(Image) + DOSHeader->e_lfanew);

我失去了太多时间来找到这个答案......