0

当我偶然发现这段代码时,我只是在检查一个简单的可执行打包器的代码,该打包器将一个部分写入可执行文件中,在启动期间将其解包:

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 开头的距离。在加载程序代码中,它被定义为无符号长整数。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);
}

虽然我知道第二个例程替换了解包器代码中的值,但我想知道为什么要完成联合的麻烦。任何帮助,将不胜感激。

4

1 回答 1

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

所以代码意图看起来像是试图掩盖大小的高阶位,尽管这非常难看并且不太可能是可移植的。

正如大卫指出的那样,您应该注意严格的别名。这篇文章Type-punning and strict-aliasing更好,因为它有一些使用unions 的真实世界问题的可靠示例。因此,为了确保此代码按预期工作,gcc或者clang您需要传入以下命令行参数-fno-strict-aliasing

于 2013-04-01T12:57:11.163 回答