【问题标题】:c++ zero out memory of a functionc ++将函数的内存归零
【发布时间】:2015-09-10 10:57:51
【问题描述】:

我有一点关于反射 dll 注入的想法。如果你不知道什么是反射dll注入,可以在这里查看:https://github.com/stephenfewer/ReflectiveDLLInjection

基本上所做的就是将 dll 加载到内存中,然后将内存注入目标进程 - 至少这是 afaik 发生的事情。我可能是错的,但这并不重要。

任务

  1. 查找给定函数的内存地址
  2. 查找给定函数的大小
  3. 使用SecureZeroMemory() 将内存清零

代码

#include "ReflectiveLoader.h"

extern HINSTANCE hAppInstance;

void Function1() { MessageBoxA(NULL, "Function 1 called!", "Test", MB_OK); }
void Function2() { MessageBoxA(NULL, "Function 2 called!", "Test", MB_OK); }
void Function3() { MessageBoxA(NULL, "Function 3 called!", "Test", MB_OK); }

void GetMemoryInformation()
{
    void(*p_Func1)() = &Function1;
    void(*p_Func2)() = &Function2;
    void(*p_Func3)() = &Function3;

    char buffer[300];
    sprintf(buffer, "Function1 - Address: %p | Size: %zu \nFunction2 - Address: %p | Size: %zu \nFunction3 - Address: %p | Size: %zu", 
        p_Func1, sizeof(p_Func1),
        p_Func2, sizeof(p_Func2),
        p_Func3, sizeof(p_Func3)
        );

    MessageBoxA(NULL, buffer, "Memory information", MB_OK);
}

void ZeroMemoryFunctions()
{
    void(*p_Func2)() = &Function2;
    void(*p_Func3)() = &Function3;

    SecureZeroMemory(p_Func2, sizeof(p_Func2));
    SecureZeroMemory(p_Func3, sizeof(p_Func3));
}

DWORD WINAPI MainThread(LPVOID PARAMS)
{
    MessageBoxA(NULL, "Main thread initialized!", "Entry", MB_OK);
    for (;;)
    {
        if (GetAsyncKeyState(0x31) & 0x8000) Function1(); // key 1
        if (GetAsyncKeyState(0x32) & 0x8000) Function2(); // key 2
        if (GetAsyncKeyState(0x33) & 0x8000) Function3(); // key 3

        if (GetAsyncKeyState(VK_UP) & 0x8000) GetMemoryInformation();
        if (GetAsyncKeyState(VK_DOWN) & 0x8000) ZeroMemoryFunctions();

        if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) MessageBoxA(NULL, "Main thread closed!", "Exit", MB_OK), ExitThread(0);

        Sleep(100);
    }
}

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved )
{
    BOOL bReturnValue = TRUE;
    switch( dwReason ) 
    { 
        case DLL_QUERY_HMODULE:
            if( lpReserved != NULL )
                *(HMODULE *)lpReserved = hAppInstance;
            break;
        case DLL_PROCESS_ATTACH:
            hAppInstance = hinstDLL;

            MessageBoxA( NULL, "Reflective Dll injected!", "Injected", MB_OK );
            CreateThread(0, 0, &MainThread, 0, 0, 0);

            break;
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
    }
    return bReturnValue;
}

它有什么作用?

  • 按键1到3,调用指定的函数,显示一个简单的消息框
  • 向上箭头显示一个消息框,其中包含每个函数的函数地址和大小
  • 向下箭头将函数 Function2()Function3() 归零
  • Escape 正常退出主线程

问题

我对内存相关的编码非常陌生,所以我很确定我做错了什么。

我认为我以错误的方式获取函数的大小,而是获取指向函数的指针的大小,对吗?

其次,如果我打电话给ZeroMemoryFunctions() 并按下键 1,它就可以工作。当我按下键 2 或 3 时,目标进程崩溃,导致函数内存清零。这意味着,SecureZeroMemory() 有效,对吗?但由于填充零的块大小错误,我相信它只是部分归零。

【问题讨论】:

  • 那么,通过编写您自己的 ld.so 等价物,这仅仅是在 Windows 上模拟 *NIX 共享对象的一次非常复杂的尝试吗?哦,你不能在调试器中检查大小和可能部分归零的内存吗?
  • sizeof(p_Func1) 肯定是指针的大小,而不是指向的函数。我不知道获得代码大小的可靠方法。崩溃本身可能是因为您没有足够的权限写入代码空间。各种防病毒功能可能会阻止您这样做。
  • 这样做的目的是,我可以在注入时从 x 个函数中选择随机函数,这些函数将在代码中使用。我想将其余的函数归零,这些函数不是随机选择的。我对用类似 c 的语言进行编码非常陌生,特别是与记忆动作相关,所以我不知道你在说什么@Useless,我很抱歉。我不确定我什至知道如何使用调试器来做到这一点,但只有 8 个字节被清零应该是合乎逻辑的,因为这是指针的大小。手头的问题是让函数的大小归零。
  • @PriitJõe - 所以你是在专门询问Task.2?
  • 我仍然不明白你通过将函数代码归零来实现什么 - 它不会释放内存,你不能调用它,如果你不调用它为什么内存清零有关系吗?

标签: c++ memory zero


【解决方案1】:

这里有几个问题:

  • 您不更改内存保护(可以/应该读取 + 执行),您需要使其可写,例如 VirtualProtect
  • 获取函数大小并非易事。函数可以/将被编译器分解成多个部分,有些可能会被内联等。

可能的解决方案

从最简单到最难排序(正确实施)。

  • 将您的函数移动到 PE 文件中的单独部分。 (例如在 msvc 中使用 alloc_text
  • 在函数后插入辅助存根,以便您可以将它们用作“大小”标记。 (见注 1)。
  • 使用像capstone这样的反汇编程序在运行时确定函数的大小

注 1:

使用这样的结构:

void function_to_erase()
{
    // your logic here
}

void function_to_erase_helper()
{
    // empty
}

您可以通过从您的函数中减去帮助程序的地址来确定function_to_erase 的大小,当且仅当您强制编译器不对这些函数重新排序时。强> (Project properties->Linker->Optimization->Function Order)

【讨论】:

  • 这实际上是有道理的,至少在某些方面,但我将不得不做更多的搜索,以完全理解所有这些,因为我对类似 C 的语言非常陌生,就像我已经在这个问题中多次提到的那样。您如何建议我在获得尺寸后删除该功能? SecureZeroMemory() 会像我在操作中那样做,还是有另一种方法可以真正从内存中擦除该部分?归零,只是用零替换字节,但如果我是正确的,那会留下一个(零)痕迹。
  • 当然,将某些东西设置为零会留下a trace of zeroes。你会期待什么,将它设置为零会在你的物理内存上烧一个洞,以确保它永远不会被发现?
  • 有趣,哈哈。对不起一个愚蠢的问题。我正在考虑可能过滤掉注入器本身中要擦除的函数,一旦它们被加载到内存中,那么它们就不会首先被注入到目标进程中。
  • “函数顺序”是否要求我制作一个列出函数确切顺序的文件,或者我可以让它使用它们在源中自动指定的顺序吗?另外,我找不到一个示例文件来说明如何指定函数的顺序。 @MarkJansen
  • 对于所有这些目的,每个功能(或功能组)的单独部分是最简单的。您可以相当容易地枚举 PE 文件中的部分。 (事实上​​,注入其他进程的代码应该已经这样做了以应用正确的内存保护标志,因此您应该能够对其进行修改,以仅选择性地将所需的部分映射到内存中)。
猜你喜欢
  • 2011-08-25
  • 1970-01-01
  • 2012-12-27
  • 2015-09-16
  • 1970-01-01
  • 2015-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多