我刚刚检查了一个简单的可执行打包程序的代码,它将一个部分写入可执行文件中,在启动时解压缩它,当我偶然发现这段代码时:
void setDistance( unsigned long size )
{
char* set = (((char *)I)+pUnpacker->VirtualAddress);
union
{
short sh[2];
long l;
} conv;
conv.l = size;
conv.sh[0] = 0;
unpacker_set(set, (char *)(&conv.l), 4, TEXT_DISTANCE);
}
Size是从内存中的解包器代码到应该解压缩的Section的开头的距离。在加载程序代码中,它被定义为unsigned long。 unpacker_set另一方面有这段代码:
void inline unpacker_set( char* at, char* what, size_t size, unsigned long sig )
{
DWORD oldprotect;
unsigned char *set = (unsigned char *)at;
while(*((unsigned long*)(set)) != sig)
set++;
if(VirtualProtect(set, size, PAGE_READWRITE, &oldprotect) == TRUE)
for(unsigned i=0; i<size; i++)
*(set+i) = *(what+i);
}
虽然我知道第二个例程替换了unpacker代码中的值,但我想知道为什么要完成与union的麻烦。任何帮助将不胜感激。
答案 0 :(得分:1)
理解代码的最好方法可能是编写一个非常小的测试用例并查看它的作用:
#include <iostream>
void f()
{
union
{
short sh[2];
long l ;
} conv ;
conv.l = 100000000 ;
std::cout << std::hex << conv.l << std::endl ;
conv.sh[0] = 0 ;
std::cout << std::hex << conv.l << std::endl ;
}
int main()
{
f() ;
}
我看到的输出如下:
5f5e100
5f50000
因此,代码意图看起来正试图掩盖大小的高阶位,尽管这非常难看并且不太可能是可移植的。
大卫指出你应该知道strict aliasing。本文Type-punning and strict-aliasing甚至更好,因为它使用union
有一些现实问题的可靠实例。因此,为了确保此代码按预期工作,假定gcc
或clang
,您需要传递以下命令行参数-fno-strict-aliasing
。