我一直在重写程序,尽管它使用哈希来对文件进行指纹识别,但我已使用IDA查找执行哈希的函数以及它将对文件执行的操作,然后再将其发送给哈希函数。 >
我只是对正在发生的事情有几个问题,我知道我可以像在DLL中一样直接调用它,但是我也想了解正在发生的事情。

unsigned int __cdecl newhash(int a1, unsigned int a2, int zero)
{
  int content; // ebx@1
  int v4; // ecx@1
  int v5; // edx@1
  int i; // eax@1
  int v7; // ecx@2
  unsigned int v8; // eax@2
  int v9; // edx@2
  int v10; // ecx@2
  int v11; // eax@2
  int v12; // edx@2
  int v13; // ecx@2
  int v14; // eax@2
  unsigned int v15; // eax@3
  int v16; // edx@15
  int v17; // ecx@15
  int v18; // eax@15
  int v19; // edx@15
  int v20; // ecx@15
  int v21; // eax@15
  int v22; // edx@15
  unsigned int contentLength; // [sp+Ch] [bp-4h]@1

  content = a1;
  contentLength = a2;
  v4 = -1640531527;
  v5 = -1640531527;
  for ( i = zero; contentLength >= 12; contentLength -= 12 )
  {
    v7 = (*(_BYTE *)(content + 7) << 24)
       + (*(_BYTE *)(content + 6) << 16)
       + (*(_BYTE *)(content + 5) << 8)
       + *(_BYTE *)(content + 4)
       + v4;
    v8 = (*(_BYTE *)(content + 11) << 24)
       + (*(_BYTE *)(content + 10) << 16)
       + (*(_BYTE *)(content + 9) << 8)
       + *(_BYTE *)(content + 8)
       + i;
    v9 = (v8 >> 13) ^ ((*(_BYTE *)(content + 3) << 24)
                     + (*(_BYTE *)(content + 2) << 16)
                     + (*(_BYTE *)(content + 1) << 8)
                     + *(_BYTE *)content
                     + v5
                     - v7
                     - v8);
    v10 = (v9 << 8) ^ (v7 - v8 - v9);
    v11 = ((unsigned int)v10 >> 13) ^ (v8 - v9 - v10);
    v12 = ((unsigned int)v11 >> 12) ^ (v9 - v10 - v11);
    v13 = (v12 << 16) ^ (v10 - v11 - v12);
    v14 = ((unsigned int)v13 >> 5) ^ (v11 - v12 - v13);
    v5 = ((unsigned int)v14 >> 3) ^ (v12 - v13 - v14);
    v4 = (v5 << 10) ^ (v13 - v14 - v5);
    i = ((unsigned int)v4 >> 15) ^ (v14 - v5 - v4);
    content += 12;
  }
  v15 = a2 + i;
  switch ( contentLength )
  {
    case 0xBu:
      v15 += *(_BYTE *)(content + 10) << 24;
      goto LABEL_5;
    case 0xAu:
LABEL_5:
      v15 += *(_BYTE *)(content + 9) << 16;
      goto LABEL_6;
    case 9u:
LABEL_6:
      v15 += *(_BYTE *)(content + 8) << 8;
      goto LABEL_7;
    case 8u:
LABEL_7:
      v4 += *(_BYTE *)(content + 7) << 24;
      goto LABEL_8;
    case 7u:
LABEL_8:
      v4 += *(_BYTE *)(content + 6) << 16;
      goto LABEL_9;
    case 6u:
LABEL_9:
      v4 += *(_BYTE *)(content + 5) << 8;
      goto LABEL_10;
    case 5u:
LABEL_10:
      v4 += *(_BYTE *)(content + 4);
      goto LABEL_11;
    case 4u:
LABEL_11:
      v5 += *(_BYTE *)(content + 3) << 24;
      goto LABEL_12;
    case 3u:
LABEL_12:
      v5 += *(_BYTE *)(content + 2) << 16;
      goto LABEL_13;
    case 2u:
LABEL_13:
      v5 += *(_BYTE *)(content + 1) << 8;
      goto LABEL_14;
    case 1u:
LABEL_14:
      v5 += *(_BYTE *)content;
      break;
    default:
      break;
  }
  v16 = (v15 >> 13) ^ (v5 - v4 - v15);
  v17 = (v16 << 8) ^ (v4 - v15 - v16);
  v18 = ((unsigned int)v17 >> 13) ^ (v15 - v16 - v17);
  v19 = ((unsigned int)v18 >> 12) ^ (v16 - v17 - v18);
  v20 = (v19 << 16) ^ (v17 - v18 - v19);
  v21 = ((unsigned int)v20 >> 5) ^ (v18 - v19 - v20);
  v22 = ((unsigned int)v21 >> 3) ^ (v19 - v20 - v21);

  return (((v22 << 10) ^ 
           (unsigned int)(v20 - v21 - v22)) >> 15) ^ 
           (v21 - v22 - ((v22 << 10) ^ (v20 - v21 - v22)));
}


a1是地址位置
a2是要哈希的文件的长度
零我重命名了,因为它总是出于任何原因发送零。 />现在要问的问题:


首先,这是像CRC或
之类的标准算法吗?
是否有理由将v4和v5变量设置为-1640531527?
(*(_BYTE *)(content + 7) << 24)的用途不是仅8位字节,所以每次都不为0吗?我查看了操作顺序,似乎先是强制转换,然后是位操作,因此这意味着它将转换为文件中的第8个字节,然后将其移位24位,对吗?为什么?
为什么有些位是带符号的而有些则是无符号的,如果有混合,为什么会改变结果?个字节并求出总数来找出文件的“哈希”,我知道切换情况正在考虑文件不能被12整除的情况。我想一旦我了解了按位运算背后的逻辑,会更清楚。

#1 楼

-1640531527是十六进制的'0x9e3779b9'。
此数字用于boost哈希函数。
函数ub4 hash( k, length, initval)中的代码至少与最后一部分类似。
我认为

据我所知,它可能是Jenkins Hash的中间变体(lookup2)。

#2 楼

一些更底层的细节:(*(_ BYTE *)(content + 7)<< 24)的目的不是只有8位字节,所以赢得了是不是每次都是0?

在C语言中,移位将操作数隐式提升为至少一个int / unsigned int,因此_BYTE值被提升为一个unsigned int。这可能是因为大多数处理器只支持单个字长而不是字节的移位。

还有另一个问题,将结果分配给int而不是unsigned int,这将带您进入下一个问题...


为什么

有符号左移和无符号左移的汇编器是相同的,因为移到寄存器外部的位会消失。这意味着反编译器无法确定左移是否为无符号,因此使用了安全的int猜测。

有符号右移和无符号右移是不同的,因为必须填充符号位正确地。这样,反编译器仅可在右移时正确猜测未签名。

通常,反编译器无法检测到变量是否为无符号变量,因为有许多操作与对有符号变量相同。

评论


我也想投票给您,并给您正确的答案!!!但是我不能投票,所以如果有人看到这个,请为我投票:)

– Krum110487
15年5月12日在17:42

不,它始终会提升为int,无论其是否有符号,除非sizeof(_BYTE)> = sizeof(int)

–phuclv
15年5月14日在7:54

#3 楼



以下移位构造(在3中提出)是将字节流转换为32位整数的一种广泛使用的方法。

   (*(_BYTE *)(content + 7) << 24)
 + (*(_BYTE *)(content + 6) << 16)
 + (*(_BYTE *)(content + 5) << 8)
 + *(_BYTE *)(content + 4)

31    24    23    16    15     8    7      0
AAAAAAAA    BBBBBBBB    CCCCCCCC    DDDDDDDD
  ^^^         ^^^         ^^^         ^^^
content[7]  content[6]  content[5]  content[4]


如果content是字节数组的地址,则可以简单地将

*(_BYTE *)(content+7)

写为
content[7]


但是,当然,您应该以与反编译器相同的方式声明content,但是反编译器仅看到一个指针,并不知道它实际上是字节数组。

评论


我发现它获取了不同的字节,我只是不了解该位移位是在int而不是8位字节上,并且由于从字节到int的转换始终将其放在右边,因此他们将其移位。因此,似乎它们在int中被反转了,对吗? 7是在左侧而不是在右侧?

– Krum110487
15年5月13日在14:19

反转取决于当前架构的字节序。由于该示例似乎是x86,所以它是little-endian,因此字节移位的结果最终将不会反转int。因此,也可以以更简单的方式((int *)content)[1]提取int,但这是独立于平台的方式。

– ebux
2015年5月14日在7:25