我一直在尝试使用“thunking”,这样我就可以将成员函数用于需要 C 函数的遗留 API。我正在尝试对此使用类似的解决方案。到目前为止,这是我的 thunk 结构:
struct Thunk
{
byte mov; // ↓
uint value; // mov esp, 'value' <-- replace the return address with 'this' (since this thunk was called with 'call', we can replace the 'pushed' return address with 'this')
byte call; // ↓
int offset; // call 'offset' <-- we want to return here for ESP alignment, so we use call instead of 'jmp'
byte sub; // ↓
byte esp; // ↓
byte num; // sub esp, 4 <-- pop the 'this' pointer from the stack
//perhaps I should use 'ret' here as well/instead?
} __attribute__((packed));
下面的代码是我的一个测试,它使用了这个 thunk 结构(但它还没有工作):
#include <iostream>
#include <sys/mman.h>
#include <cstdio>
typedef unsigned char byte;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#include "thunk.h"
template<typename Target, typename Source>
inline Target brute_cast(const Source s)
{
static_assert(sizeof(Source) == sizeof(Target));
union { Target t; Source s; } u;
u.s = s;
return u.t;
}
void Callback(void (*cb)(int, int))
{
std::cout << "Calling...\n";
cb(34, 71);
std::cout << "Called!\n";
}
struct Test
{
int m_x = 15;
void Hi(int x, int y)
{
printf("X: %d | Y: %d | M: %d\n", x, y, m_x);
}
};
int main(int argc, char * argv[])
{
std::cout << "Begin Execution...\n";
Test test;
Thunk * thunk = static_cast<Thunk*>(mmap(nullptr, sizeof(Thunk),
PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0));
thunk->mov = 0xBC; // mov esp
thunk->value = reinterpret_cast<uint>(&test);
thunk->call = 0xE8; // call
thunk->offset = brute_cast<uint>(&Test::Hi) - reinterpret_cast<uint>(thunk);
thunk->offset -= 10; // Adjust the relative call
thunk->sub = 0x83; // sub
thunk->esp = 0xEC; // esp
thunk->num = 0x04; // 'num'
// Call the function
Callback(reinterpret_cast<void (*)(int, int)>(thunk));
std::cout << "End execution\n";
}
如果我使用该代码;Test::Hi
我在函数中收到分段错误。原因很明显(一旦你分析了 GDB 中的堆栈),但我不知道如何解决这个问题。堆栈未正确对齐。
x
参数包含垃圾,但y
参数包含指针this
(参见Thunk
代码)。这意味着堆栈错位了 8 个字节,但我仍然不知道为什么会这样。谁能说出为什么会这样?x
并且y
应该分别包含34
和71
。
注意:我知道这并不适用于所有场景(例如 MI 和 VC++ thiscall 约定),但我想看看我是否可以完成这项工作,因为我会从中受益匪浅!
编辑:显然我也知道我可以使用静态函数,但我认为这更像是一个挑战......