我有一块由外部代码填充的内存,我试图进行逆向工程。我不知道这个内存的完整结构,但我知道一些字段(例如,块开始时使用名为' foo'的int32,并且在偏移0xC处有一个双重调用'杆&#39)。我想定义一个结构,并基本上将一个指向这个内存块的指针重新解释为该结构,然后将它排成一行。我不确定这种技术是否有更传统的名称,但我将其称为创建“覆盖类型”。
这是我希望能够做到的草图:
START_OVERLAY_TYPE(my_type, 0xFF) // struct named my_type, size 0xFF
FIELD(0x00, int32_t foo); // field int32_t foo at 0x00
FIELD(0x0C, double bar); // field double bar at 0x0C
END_OVERLAY_TYPE
不必使用宏将是一个加分,但我没有看到他们周围的好方法。
使用我当前的实现,我将其扩展为(类似):
__pragma(pack(push, 1))
template<size_t p> struct padding_t { unsigned char pad[p]; };
template<> struct padding_t<0> {};
struct my_type
{
union
{
struct : padding_t<0xFF> {}; // ensure total size is 0xFF
struct : padding_t<0x00> { int32_t foo; }; // field at 0x00
struct : padding_t<0x0C> { double bar; }; // field at 0x0C
};
};
__pragma(pack(pop))
这编译并且工作得很好,至少在我尝试过的clang,gcc和VC ++版本中(对pragma进行了适当的更改)。不幸的是,由于匿名结构的非标准使用,警告比比皆是。
在保持标准的同时,有没有办法达到同样的效果?要求是声明(就像当前的宏一样)相当简单,而对于消费者来说,使用是无法区分的
struct my_type { int32_t foo; double bar; }
至少对于不经意的观察者来说。
目前的代码可以用于我的目的,我只是好奇我是否有更好的方法。
答案 0 :(得分:2)
您可以尝试使用隐式类型转换和包含该值的内部结构的赋值运算符。这种方式不是使用未命名的结构,而是带有名称,但内部通过运算符重载成为未命名的部分。
我尝试了一些客户端代码(传递给函数,获取/设置值),一切似乎都很好。我当然可能在某个地方错过了一个场景。
__pragma(pack(push, 1))
template<size_t p, typename t>
struct padding_t
{
unsigned char pad[p];
t val;
operator t () const {return val;}
operator t& () {return val;}
padding_t<p, t>& operator= (const t& rhs) {val = rhs; return *this;}
};
template<typename t> struct padding_t<0, t>
{
t val;
operator t () const {return val;}
operator t& () {return val;}
padding_t<0, t>& operator= (const t& rhs) {val = rhs; return *this;}
};
template<size_t p>
struct sizing_t
{
unsigned char pad[p];
};
struct my_type
{
union
{
sizing_t<0xFF> size; // ensure total size is 0xFF
padding_t<0x00, int32_t> foo; // field at 0x00
padding_t<0x0C, double> bar; // field at 0x0C
};
};
__pragma(pack(pop))