【问题标题】:Access violation when calling copied function调用复制函数时访问冲突
【发布时间】:2020-08-11 17:04:37
【问题描述】:

这是一个将函数复制到堆上,将其设置为可执行并调用它的程序。

#include <iostream>
#include <iomanip>
#include <csignal>
#include <Windows.h>
using std::cout;

#define RET 0xC3

void printBytes(void* start, uintptr_t numBytes) {
    std::ios_base::fmtflags savedFlags(cout.flags());
    cout << std::hex << std::uppercase << std::setfill('0');

    bool lineComplete = false;
    for (unsigned int byte = 0; byte < numBytes; byte++) {
        lineComplete = byte % 4 == 3;
        cout << std::setw(2)
            << (int)*((uint8_t*)start + byte)
            << (lineComplete ? '\n' : ' ');
    }

    cout << (lineComplete ? "\n" : "\n\n");

    cout.flags(savedFlags);
}

uint8_t* findByte(void* start, uint8_t targetByte) {
    uint8_t* pByte = (uint8_t*) start;
    while (*pByte != targetByte) pByte++;
    return pByte;
}

uintptr_t findByteOffset(void* base, uint8_t targetByte) {
    uint8_t* byte = findByte(base, targetByte);
    return (uintptr_t)byte - (uintptr_t)base;
}

int main() {
    void(*function)() = [] { cout << "Hello world"; };
    uintptr_t size = findByteOffset(function, RET) + 1;
    cout << "function : " << function << "\n\n";
    printBytes(function, size);

    void(*functioncopy)() = static_cast<void(*)()>(malloc(size));
    cout << "functioncopy : " << functioncopy << "\n\n";

    if (functioncopy) 
    {
        memcpy(functioncopy, function, size);
        printBytes(functioncopy, size);

        DWORD oldProtect;
        VirtualProtect(functioncopy, size, PAGE_EXECUTE_READWRITE, &oldProtect);
        cout << "functioncopy()\n";
        functioncopy();
        VirtualProtect(functioncopy, size, oldProtect, &oldProtect);
    }
    else cout << "malloc(" << size << ") failed.";
}

当我运行程序时(在 Release 或 Debug 配置中,有或没有优化),它会在 functioncopy() 中产生访问冲突。我不知道为什么。

【问题讨论】:

  • 您为什么希望这会起作用?!您能否指出任何声称可以复制已编译函数并执行该副本的参考或文档?
  • 你这样做是为了好玩吗?或者您是否正在尝试解决一些问题。这感觉就像XY problem
  • @ThomasJager 很遗憾我不能透露生产代码的细节。
  • @Discape 在基本上任何生产代码中做这种事情感觉都是一个糟糕的主意。即使它是反作弊/混淆/反逆向工程系统的一部分,也要仔细考虑你希望你的代码库有多脆弱。此外,为了获得有用的帮助来解决您的问题,您可能必须找到某种方法来演示等效的问题。
  • 关键问题是函数调用通常会编译为call rel32。将其复制到其他地方意味着相同的相对偏移量加上不同的起始地址将指向不同的绝对地址。 (或者调用DLL函数,我认为Windows会倾向于在x86-64上使用call [RIP + rel32]间接调用,同样的问题。)

标签: c++ windows assembly


【解决方案1】:

绝对没有理由期望这会奏效。无论是否有人能想到它不起作用的原因,根本没有理由期望它起作用。没有标准或参考表明这应该起作用,并且期望它起作用是直率的,疯狂的。

这只是它可能失败的最明显的方式:如果 0xc3 恰好作为其他东西的一部分出现在函数的代码中,可能是它调用的辅助函数的地址怎么办?如果它不使用RET 指令返回怎么办?如果函数的代码使用相对跳转到翻译单元中的另一个函数怎么办?如果函数跳转并且它的一些代码位于内存中的RET 之后怎么办?如果还有一百万件我现在想不出来的错误怎么办?

这段代码太荒谬了。它不是基于任何合理的工程推理。

【讨论】:

  • "使用相对跳转" -- 请注意,近乎立即的call 指令也是相对的,因此调用任何其他函数通常也属于这种情况。
  • @Discape - assembly - 这个变化是什么?只有深厚的知识才能完成这项工作
  • @RbMm 如果我让它尽可能独立于位置,然后手动修补其余部分
  • @RbMm pastebin.com/JKtKCpyh 看起来我的知识很深
  • @Discape - 抱歉,没有
猜你喜欢
  • 1970-01-01
  • 2014-03-19
  • 2022-11-03
  • 1970-01-01
  • 2018-11-11
  • 1970-01-01
  • 2018-02-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多