【问题标题】:Code injection in a MFC applicationMFC 应用程序中的代码注入
【发布时间】:2012-08-13 03:30:49
【问题描述】:

我有一个获取 MFC 应用程序句柄的 Win32 应用程序。我的目标是强制 MFC 程序不显示 ASSERT 错误消息框。

基本上,我已经制作了一个原型,允许我的 Win32 应用程序强制 MFC 应用程序显示一个消息框,只是为了检查这个想法是否可行。现在我需要强制 MFC 应用程序不显示此类 ASSERT 错误消息框。

这可能吗?

【问题讨论】:

  • 当然,只要获取应用程序的源并修复它。 :-) 我猜你可以在 OllyDbg 中打开可执行文件,找到警告对话框文本,这样你就可以看到哪些指令引用了它并 NOP 出来(通过修改可执行文件或在附加到进程后修补它们)。跨度>
  • 或者您可以禁用每个 MessageBox 调用,只需将导入表中对消息框显示函数的引用替换为您自己的函数即可。这可以自动化
  • @dvvrd 你的解决方案听起来很有希望,你的意思是在我的 Win32 代码中我可以调用一个函数来替换 MFC 应用程序调用的显示函数吗?
  • 是的,您只需要解析您的 MFC 应用 PE 文件,在那里找到导入表并将其替换为您想要的地址。我曾经写过这样的东西,如果你愿意,我可以在这里发布代码
  • @dvvrd:我也喜欢看你的代码。

标签: c++ winapi mfc


【解决方案1】:

您可以通过拦截MessageBoxA/MessageBoxW 函数调用来做到这一点。在用户模式级别,这通常在以下三个位置之一完成:

  • 调用站点 - 在您的可执行文件中可能不止一次调用MessageBox。您需要找到要禁用的那个。然后你可以用什么都不做的代码覆盖调用(即用nop指令覆盖)。
  • IAT - 导入地址表;由 PE 加载器填充的函数指针表。执行经常(但并非总是)流经此处,替换 MessageBox 的函数指针可以允许将 MessageBox 调用重定向到一些什么都不做的例程。
  • 函数入口点 - MessageBox 函数的开始。这可以通过GetProcAddress 定位,并将第一条指令替换为ret

操作在运行时(动态)或静态(二进制重写/可执行编辑)完成,第一个选项更为常见。可以帮助您实现运行时绕行的库是 Microsoft Detours。

这不是所有可能性的完整列表,而是执行重定向和绕行的最常见方法。

【讨论】:

    【解决方案2】:

    非常遗憾我错过了该代码。但是,您仍然可以手动完成。

    1. 下载安装CFF explorer
    2. 用它打开你的 exe 文件
    3. 在部分资源管理器中选择导入目录。
    4. 在导入的dll列表中选择USER32.dll
    5. 选择 MessageBoxA 或 MessageBoxW。编辑 OFT 列。在那里写一些“无害”功能的OFT。我以 GetWindowRect 为例。

    如果您仍然希望应用程序执行此操作,我有一个功能非常相似的代码。它只是将您的 dll 嵌入到导入表中。您既可以对其进行编辑以达到想要的结果,也可以使用它将 MessageBoxW 调用重定向到您的处理程序。

    #include <windows.h>
    #include <tchar.h>
    #include "stdafx.h"
    #include <stdio.h>
    
    DWORD MapFile(HANDLE &FileMapping, LPVOID &FileBegin, const _TCHAR *exeName) {
        HANDLE File = CreateFile(exeName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
                                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
        if (File == INVALID_HANDLE_VALUE) {
            return GetLastError();
        }   
    
        FileMapping = CreateFileMapping(File, NULL, PAGE_READWRITE, 0, 0, NULL);
        CloseHandle(File);
    
        if (!FileMapping) {
            return GetLastError();
        }
    
        FileBegin = MapViewOfFile(FileMapping, FILE_MAP_WRITE, 0, 0, 0);
        if (!FileBegin) {
            CloseHandle(FileMapping);
            return GetLastError();
        }
    
        return 0;
    }
    
    DWORD RewriteImportTable(const HANDLE FileMapping, const LPVOID FileBegin, const _TCHAR *dllName, const _TCHAR *funcName, DWORD &finalResult) {
    
        IMAGE_DOS_HEADER* dos_header;
        IMAGE_FILE_HEADER* file_header;
        IMAGE_OPTIONAL_HEADER* optional_header;
        IMAGE_SECTION_HEADER* section_header;
    
        // Counting PE-header offset
        dos_header = (IMAGE_DOS_HEADER*) FileBegin;
        DWORD PEOffset = dos_header->e_lfanew;
        file_header = (IMAGE_FILE_HEADER*) ((DWORD)FileBegin + PEOffset); // file_header must reference "PE\0"
    
        // Checking if we work with PE
        _TCHAR* PEString = "PE\0";
        if (_tcscmp(PEString, (const _TCHAR*) file_header) != 0) {
            printf("This file is not Portable Executable!\n");
            return 666;
        }
    
        file_header = (IMAGE_FILE_HEADER *)((DWORD)file_header + sizeof(DWORD)); // Ignoring PE
        optional_header = (IMAGE_OPTIONAL_HEADER *)((DWORD)file_header + sizeof(IMAGE_FILE_HEADER));
    
        // Finding import section
        DWORD ImportRVA = optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
        int sectNum = -1;
    
        // Finding import table
        section_header = (IMAGE_SECTION_HEADER*) ((DWORD) optional_header + sizeof(IMAGE_OPTIONAL_HEADER));
    
        for (int i = 0; i < (file_header->NumberOfSections); i++) {
            if (ImportRVA < (section_header->VirtualAddress)) {
                section_header--;
                sectNum = i-1;
                break;  
            }
            section_header++;
        }
    
        if (sectNum == -1) {
            printf("This program uses no external libraries! (strange)\n");
            return 666;
        }
    
        // Getting address of section folowing import section
        section_header++;
        DWORD SectionNextToImportBegin = (DWORD)FileBegin + section_header->PointerToRawData;
        section_header--;
    
        // Getting the address of the import table
        LPVOID ImportSectionBegin = (LPVOID) ((DWORD)FileBegin + section_header->PointerToRawData);
    
        // Counting the import table offset in the import section
        LPVOID ImportTable = (LPVOID)((DWORD)ImportSectionBegin + (ImportRVA - section_header->VirtualAddress));
    
        IMAGE_IMPORT_DESCRIPTOR *DLLInfo = (IMAGE_IMPORT_DESCRIPTOR*) ImportTable;
        LPVOID DLLName;
        DWORD DLLCounter = 0;
    
        while (DLLInfo->Name != NULL) {
            DLLCounter++;
            DLLName = (LPVOID) ((DWORD)ImportSectionBegin + ((DWORD)DLLInfo->Name - section_header->VirtualAddress));
            DLLInfo++;
        }
    
        printf("Number of imported libraries: %d\n", DLLCounter);
    
        // Counting the size of the future import table
        DWORD newImportTableSize = sizeof(IMAGE_IMPORT_DESCRIPTOR) * (DLLCounter + 2);
    
        // Finding the end of the import section
        LPVOID pos = (LPVOID) (SectionNextToImportBegin - 1);
    
        DWORD maxFree = 0;
        DWORD prevPtr;
        LPVOID freePtr = NULL;
    
        // Searching for the free place
        while (pos >= ImportSectionBegin) {
            if (*(BYTE*)pos == 0) {
                prevPtr = (DWORD) pos;
    
                while (*(BYTE*)pos == 0) {
                    pos = (LPVOID) ((DWORD)pos - 1);
                }
    
                if (((DWORD)prevPtr - (DWORD)pos) > maxFree) {
                    maxFree = ((DWORD)prevPtr - (DWORD)pos);
                    freePtr = (LPVOID) ((DWORD)pos + 1);
                }
            }
            pos = (LPVOID) ((DWORD)pos - 1);
        }
    
        // Modifying pointer: it can refer the tailing zero of some stucture
        freePtr = (LPVOID) ((LPDWORD)freePtr + 1);
        maxFree -= 4;
    
        // Checking if we have enough space in the import section
        if (maxFree < newImportTableSize) {
            printf("Not enough free space in Import Section\n");
            return 666;
        }
    
        printf("Injecting new library...\n");
    
        // Copying old import table on the new place
        memcpy(freePtr, ImportTable, sizeof(IMAGE_IMPORT_DESCRIPTOR) * DLLCounter);
    
        // Saving everithing we need on the old place
        typedef struct {
            DWORD ZeroDword;
            DWORD IAT;
            DWORD IATEnd;
        } MeanStruct;
    
        MeanStruct patch;
        patch.ZeroDword = NULL; // this is \0 for dll name
        patch.IAT = ImportRVA + _tcslen(dllName) + sizeof(MeanStruct); // RVA to where list of functions begins
        patch.IATEnd = NULL;
    
        WORD Hint = 0;
    
        IMAGE_IMPORT_BY_NAME myName;
        myName.Hint = 0x00;
        myName.Name[0] = 0x00;
    
        LPDWORD zeroPtr = (LPDWORD) ImportTable;
        memcpy(zeroPtr, dllName, _tcslen(dllName));
        zeroPtr = (LPDWORD) ((DWORD)zeroPtr + strlen(dllName));
        memcpy(zeroPtr, &patch, sizeof(patch));
        zeroPtr = (LPDWORD) ((DWORD)zeroPtr + sizeof(patch));
    
        finalResult = (DWORD)zeroPtr - (DWORD)ImportSectionBegin + section_header->VirtualAddress;
    
        memcpy(zeroPtr, &Hint, sizeof(WORD));
        zeroPtr = (LPDWORD) ((DWORD)zeroPtr + sizeof(WORD));
        memcpy(zeroPtr, funcName, strlen(funcName) + 1); // we have no need to write \0 into the end - this is already free space
        zeroPtr = (LPDWORD) ((DWORD)zeroPtr + strlen(funcName) + 1);
        memcpy(zeroPtr, &myName, sizeof(IMAGE_IMPORT_BY_NAME));
    
        // filling info about dll
        IMAGE_IMPORT_DESCRIPTOR myDLL;
    
        // counting RVA for IMAGE_IMPORT_BY_NAME: 
        DWORD IIBN_Table = ImportRVA + strlen(dllName) + sizeof(DWORD);
    
        // function name pointer
        myDLL.Characteristics = IIBN_Table;
        myDLL.TimeDateStamp = NULL;
        myDLL.ForwarderChain = NULL;
        // dll name pointer
        myDLL.Name = ImportRVA;
        myDLL.FirstThunk = IIBN_Table;
    
        // writting dll info into the new import table
        LPVOID oldFreePtr = freePtr;
        freePtr = (LPVOID) ((DWORD)freePtr + sizeof(IMAGE_IMPORT_DESCRIPTOR) * DLLCounter);
        memcpy(freePtr, &myDLL, sizeof(IMAGE_IMPORT_DESCRIPTOR));
    
        // creating list tail
        myDLL.Characteristics = NULL;
        myDLL.TimeDateStamp = NULL;
        myDLL.ForwarderChain = NULL;
        myDLL.Name = NULL;
        myDLL.FirstThunk = NULL;
    
        // writing list tail
        freePtr = (LPVOID) ((DWORD)freePtr + sizeof(IMAGE_IMPORT_DESCRIPTOR));
        memcpy(freePtr, &myDLL, sizeof(IMAGE_IMPORT_DESCRIPTOR));
    
        // setting new import table rva
        DWORD newImportTableRVA = (DWORD)oldFreePtr - (DWORD)ImportSectionBegin + section_header->VirtualAddress;
    
        // changing DataDirectory
        optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = newImportTableRVA;
        optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (DLLCounter + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
    
        // clearing non-actual values
        optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
        optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
    
        optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0;
        optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0;
    
        return 0;
    }
    
    int _tmain(int argc, _TCHAR *argv[]) {
    
        if (argc != 4) {
            printf("Invalid arguments number!!!\n");
            return 0;
        }
    
        HANDLE FileMapping;
        LPVOID FileBegin;
        DWORD FileMappingResult = MapFile(FileMapping, FileBegin, argv[1]);
        if (0 != FileMappingResult) {
            printf("Error of file mapping (%d)\n", FileMappingResult);
            if (NULL != FileMapping) CloseHandle(FileMapping);
            return FileMappingResult;
        }
    
        DWORD functionAddr;
        DWORD RewriteImportTableResult = RewriteImportTable(FileMapping, FileBegin, argv[2], argv[3], functionAddr);
        if (0 != RewriteImportTableResult) {
            UnmapViewOfFile(FileBegin);
            CloseHandle(FileMapping);
            return 666;
        }
    
        printf("Library successfully injected!\n");
        printf("Address of injected function: %X", functionAddr);
    
        UnmapViewOfFile(FileBegin);
        CloseHandle(FileMapping);
    
        return 0;
    }
    

    【讨论】:

    • 请注意,并非所有呼叫都会通过 IAT。例如,如果通过GetProcAddress动态找到地址
    • @Mike Kwan:很好的说明,但目标是禁用断言消息框。他们的电话将通过 IAT
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 2018-06-18
    • 1970-01-01
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多