PE文件头的校验和在汇编里的算法到底咋写呢?

时间:2022-08-22 10:00:09
PE结构网上到处都是,关于其中的扩展头(也叫可选头),其中有个成员CheckSum(相对于本结构的 40h偏移处,是结构的第22个成员),按描述说是PE的校验和,一般不使用,对于所有驱动、系统启动时加载的DLL、系统关键进程加载的DLL都要进行校验和的校验;
这个校验和可以通过API计算:CheckSumMappedFile 或者直接 MapFileAndCheckSum;
但是不少资料和书籍也说了其算法比较简单,自己手动写代码计算也可以,描述的计算原理一般是这样:
1、因为要计算整个文件数据嘛,所以开始这个字段的数据要先置0;
2、从文件头开始,每次读一个字(WORD),进行带进位的累加(ADC),超出WORD部分自动溢出;
3、将前面的累加和再加上PE文件的长度,结果就是这个校验和了。
(以上资料基本摘自戚利《Windows PE 权威指南》,在网上其他资料与其描述也基本相同);
比如这个连接的算法,在网上就随处可见,大部分都是这么算的:
http://sec.chinabyte.com/497/8918497.shtml
我简单写一下刚开始我按照上述资料写的代码:

;首先假设我已经把PE文件映射的起始地址放入ebx了!
mov ecx, _dwSize         ;PE文件大小
shr ecx, 01h                   ;因为每次读2字节,所以循环次数是长度的一半。部分资料为了处理奇数在这里要+1再右移或其他方法,但本帖我就找偶数长度PE测试也是不对的,所以这里就不写那些兼容处理部分了;
xor edx, edx
xor eax, eax
clc
@@:
mov ax, word ptr [ebx]
adc dx, ax
add ebx, 02h
loop @B
mov eax, edx
add eax, _dwSize         ;这样就算是计算完毕了。


但是我测试,这个算法根本不对(跟API计算的结果对比的话),差距很大的哦;
后来在CSDN里找到这个:
http://bbs.csdn.net/topics/20067469#post-12158577
我按照这位前辈的写法修改了上面:

;首先假设我已经把PE文件映射的起始地址放入ebx了!
mov ecx, _dwSize
shr ecx, 01h
xor edx, edx
clc
@@:
;------------------------------------
movzx eax, word ptr [ebx]
add ebx, eax
mov eax, edx
and edx, 0ffffh
shr eax, 10h
add edx, eax
;------------------------------------
add ebx, 02h
loop @B
mov eax, edx
add eax, _dwSize

我测试这样算出来的跟API比才是对的,但是我发帖子就想问问,这到底是个啥原理啊?
好多的教程、书籍都是些挺有名的前辈高人所写,不可能他们说错吧(这里的错1是:带进位加法,而第二段代码根本没有adc;2是word超出部分自动溢出,但第二段代码没有让其溢出,这里我有点看太懂,好像是把高16位和低16位相加了吧?)
谁能给解释解释?莫非这个算法微软后来又更新了?还有第二段代码的原理是什么,我标记出来有改动的部分,那些虽然指令简单,但逻辑上我不太理解。。。
多谢您指教了!

5 个解决方案

#1


这个计算方面满怪异的,核心部分是 word,后来因文件大小的关系才升级到了 dword。
看第二段代码,加法运算进行了两次吧,第一次就是进行累加,第二次应该是将累加结果的 HiWord (shr eax, 10h) 和 LoWord (and edx, 0ffffh)  相加,结果在 edx 里。带进位加的问题,应该是第二次的加运算吧,由于是两个 word 相加到个 dword 里,所以进位是自然而无须 adc 的。自动的溢出,应该是第一次的加运算,简单的 add 了,没有做任何其它的处理。

#2


第二个代码:
9 add ebx, eax
这应该是add edx,eax吧。

你的代码:
adc dx, ax
应该是
add dx, ax
adc dx,0
这样才合理。

#3


引用 2 楼 DelphiGuy 的回复:
第二个代码:
9 add ebx, eax
这应该是add edx,eax吧。

你的代码:
adc dx, ax
应该是
add dx, ax
adc dx,0
这样才合理。

感谢,您说对了,我第二段代码是看着我的源码自己手动打出来的,确实打错了,应该是 add edx,eax;
我实际调试的源码里也是正确的,所以我测试的结果正确;
而您回复的第二个观点,我测试了下,还是不行,结果是错误的,其实我个人感觉:
adc dx, ax

add dx, ax
adc dx,0
这两种写法唯一的差别只在循环的最后一次吧,因为前面的就算没你这个 adc dx,0 ,也会在下一次 adc dx, ax 将CF中的那个1给加进来,而原先我测试的就是错误的结果与正确的相差可不是1、2这么点的值,而是相差大约1倍左右。。。

#4


非也,按你的指令顺序,后面的add ebx, 02h把上一次adc dx, ax可能产生的进位清掉了,下一次也加不上。
另外,我测试了一下,按我的改法算出来的checksun和第二种方法算出来的是一致的。

#5


引用 4 楼 DelphiGuy 的回复:
非也,按你的指令顺序,后面的add ebx, 02h把上一次adc dx, ax可能产生的进位清掉了,下一次也加不上。
另外,我测试了一下,按我的改法算出来的checksun和第二种方法算出来的是一致的。

真实一语惊醒梦中人啊。。。。我说为什么不成功呢?就是因为下面那个 add ebx, 02h 搞的,迷糊了,adc 这种东西完全需要考虑前后的其他指令能不能改变了CF我擦。。。。
我最终改成这样了,比较方便(其实人家戚利的书上也是利用esi的,只不过我一般习惯能不用字符串的寄存器就不用,没想到竟然会有这种差异。。。)

shr ecx, 01h            ;esi中已经存放PE映射的首地址了,ecx为PE长度的一半,先不考虑奇数,最后处理。。。
xor edx, edx
clc
@@:
lodsw
adc dx, ax
loop @B
pop ecx
push ecx
and ecx, 01h ;处理奇数
jecxz @F
lodsb
adc dl, al


结果完全正确,结贴给分啦。。。

#1


这个计算方面满怪异的,核心部分是 word,后来因文件大小的关系才升级到了 dword。
看第二段代码,加法运算进行了两次吧,第一次就是进行累加,第二次应该是将累加结果的 HiWord (shr eax, 10h) 和 LoWord (and edx, 0ffffh)  相加,结果在 edx 里。带进位加的问题,应该是第二次的加运算吧,由于是两个 word 相加到个 dword 里,所以进位是自然而无须 adc 的。自动的溢出,应该是第一次的加运算,简单的 add 了,没有做任何其它的处理。

#2


第二个代码:
9 add ebx, eax
这应该是add edx,eax吧。

你的代码:
adc dx, ax
应该是
add dx, ax
adc dx,0
这样才合理。

#3


引用 2 楼 DelphiGuy 的回复:
第二个代码:
9 add ebx, eax
这应该是add edx,eax吧。

你的代码:
adc dx, ax
应该是
add dx, ax
adc dx,0
这样才合理。

感谢,您说对了,我第二段代码是看着我的源码自己手动打出来的,确实打错了,应该是 add edx,eax;
我实际调试的源码里也是正确的,所以我测试的结果正确;
而您回复的第二个观点,我测试了下,还是不行,结果是错误的,其实我个人感觉:
adc dx, ax

add dx, ax
adc dx,0
这两种写法唯一的差别只在循环的最后一次吧,因为前面的就算没你这个 adc dx,0 ,也会在下一次 adc dx, ax 将CF中的那个1给加进来,而原先我测试的就是错误的结果与正确的相差可不是1、2这么点的值,而是相差大约1倍左右。。。

#4


非也,按你的指令顺序,后面的add ebx, 02h把上一次adc dx, ax可能产生的进位清掉了,下一次也加不上。
另外,我测试了一下,按我的改法算出来的checksun和第二种方法算出来的是一致的。

#5


引用 4 楼 DelphiGuy 的回复:
非也,按你的指令顺序,后面的add ebx, 02h把上一次adc dx, ax可能产生的进位清掉了,下一次也加不上。
另外,我测试了一下,按我的改法算出来的checksun和第二种方法算出来的是一致的。

真实一语惊醒梦中人啊。。。。我说为什么不成功呢?就是因为下面那个 add ebx, 02h 搞的,迷糊了,adc 这种东西完全需要考虑前后的其他指令能不能改变了CF我擦。。。。
我最终改成这样了,比较方便(其实人家戚利的书上也是利用esi的,只不过我一般习惯能不用字符串的寄存器就不用,没想到竟然会有这种差异。。。)

shr ecx, 01h            ;esi中已经存放PE映射的首地址了,ecx为PE长度的一半,先不考虑奇数,最后处理。。。
xor edx, edx
clc
@@:
lodsw
adc dx, ax
loop @B
pop ecx
push ecx
and ecx, 01h ;处理奇数
jecxz @F
lodsb
adc dl, al


结果完全正确,结贴给分啦。。。