从 C 中的被调用者更改调用者中的局部变量?

逆向工程 x86 linux C 堆栈变量
2021-07-10 05:47:51

我正在实现一个将执行一些耗时任务的函数,我想通过更改指示我的函数开始执行的 CPU 时间的变量来稍微修改基准测试部分。这是代码:

#if defined(_MSC_VER)
#pragma intrinsic(__rdtsc)
#else
static __inline__ uint64_t __rdtsc() {
    uint32_t hi, lo;
    __asm__ volatile
        (
        "rdtsc"
        : "=a"(lo), "=d"(hi)
        );
    return (((uint64_t) hi) << 32) | ((uint64_t) lo);
}
#endif


int arg = 42;

uint64_t start_tSC, end_tSC;

// Get total clock cycles for func
start_tSC = __rdtsc();
func(arg);
end_tSC = __rdtsc();
double func_tSC = end_tSC - start_tSC;  // <---- let's minimize this

基本上,我想设置start_tSC之前的所有任务已完成,当前时钟时间戳右func

void func(int arg) {
    perform_tasks(arg);
    /* black magic that resets start_tSC in the CALLER */
}

由于局部变量和函数参数倾向于存储在堆栈中,我可以start_tSC通过查看 的地址以某种方式推断出驻留的位置arg吗?


系统信息

~ $ uname -a
Linux hive30 4.15.0-36-generic #39~16.04.1-Ubuntu SMP Tue Sep 25 08:59:23 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

另请注意,我可以以普通用户身份访问目标系统,这意味着我可以构建可执行文件并通过gcc -S. 如果相关,这里是一些硬件信息

1个回答

编辑:
为了使偏移量与您的堆栈帧大小无关,我首先读取调用者的堆栈帧指针的地址(调用者的 ebp,位于您的返回地址之前,位于DWORD PTR [ebp])。

您不能假设 的偏移量start_tSC不会改变,因为您的代码可以使用不同版本的基准测试运行。

在解释这个想法,它的假设下运作的呼吁[ GetSystemTimeAsFileTime__rdtscGetTickCount或任何其他功能]在呼叫你的函数之前执行的权利。这种假设的理由是,在执行基准测试时,精度是必不可少的,时间往往会尽可能接近地包装测试代码。

基本思想是找到包含从计时函数返回的值的变量。
您可以从内存中读取返回地址(at DWORD PTR [ebp + 4])并尝试在您的函数之前反汇编代码,直到找到调用指令(跳过对您自己的函数的调用,因为返回地址指向此调用之后)。调用后的指令应该类似于:

MOV [ebp - offset], eax

你可以start_tSC从这里推断出偏移量

请注意,向后反汇编代码并不简单,因为 x86 代码没有固定长度的操作码。
另请注意,此解决方案需要更多代码,从而(可能)大量扩展函数大小。