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

shier2817 2014-12-18 10:22:26
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位相加了吧?)
谁能给解释解释?莫非这个算法微软后来又更新了?还有第二段代码的原理是什么,我标记出来有改动的部分,那些虽然指令简单,但逻辑上我不太理解。。。
多谢您指教了!
...全文
669 5 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
「已注销」 2014-12-19
  • 打赏
  • 举报
回复
引用 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
结果完全正确,结贴给分啦。。。
  • 打赏
  • 举报
回复
非也,按你的指令顺序,后面的add ebx, 02h把上一次adc dx, ax可能产生的进位清掉了,下一次也加不上。 另外,我测试了一下,按我的改法算出来的checksun和第二种方法算出来的是一致的。
zara 2014-12-18
  • 打赏
  • 举报
回复
这个计算方面满怪异的,核心部分是 word,后来因文件大小的关系才升级到了 dword。
看第二段代码,加法运算进行了两次吧,第一次就是进行累加,第二次应该是将累加结果的 HiWord (shr eax, 10h) 和 LoWord (and edx, 0ffffh) 相加,结果在 edx 里。带进位加的问题,应该是第二次的加运算吧,由于是两个 word 相加到个 dword 里,所以进位是自然而无须 adc 的。自动的溢出,应该是第一次的加运算,简单的 add 了,没有做任何其它的处理。
「已注销」 2014-12-18
  • 打赏
  • 举报
回复
引用 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倍左右。。。
  • 打赏
  • 举报
回复
第二个代码: 9 add ebx, eax 这应该是add edx,eax吧。 你的代码: adc dx, ax 应该是 add dx, ax adc dx,0 这样才合理。
第一卷************** 不错的PDF电子书,共3个分卷,点我名字可以找全 第1部分 逆向101 第1章 基础 3 1.1 什么是逆向工程 3 1.2 软件逆向工程:逆向 4 1.3 逆向应用 4 1.3.1 与安全相关的逆向 5 1.3.2 软件开发中的逆向 8 1.4 底层软件 9 1.4.1 汇编语言 10 1.4.2 编译器 11 1.4.3 虚拟机和字节码 12 1.4.4 操作系统 13 1.5 逆向过程 13 1.5.1 系统级逆向 14 1.5.2 代码级逆向 14 1.6 工具 14 1.6.1 系统监控工具 15 1.6.2 反汇编器 15 1.6.3 调试器 15 1.6.4 反编译器 16 1.7 逆向合法吗? 17 1.7.1 互操作性 17 1.7.2 竞争 18 1.7.3 版权法 19 1.7.4 商业机密和专利权 20 1.7.5 美国数字千禧版权法 20 1.7.6 DMCA案例 22 1.7.7 许可证协议 23 1.8 代码范例与工具 23 1.9 结论 23 第2章 底层软件 25 2.1 高阶视角 26 2.1.1 程序结构 26 2.1.2 数据管理 29 2.1.3 控制流 32 2.1.4 高级语言 33 2.2 低阶视角 37 2.2.1 底层数据管理 37 2.2.2 控制流 43 2.3 汇编语言101 44 2.3.1 寄存器 44 2.3.2 标志位 46 2.3.3 指令格式 47 2.3.4 基本指令 48 2.3.5 范例 52 2.4 编译器和编译入门 53 2.4.1 定义编译器 54 2.4.2 编译器架构 55 2.4.3 列表文件 58 2.4.4 专用编译器 59 2.5 执行环境 60 2.5.1 软件执行环境(虚拟机) 60 2.5.2 现代处理器的硬件执行环境 63 2.6 结论 68 第3章 Windows基础知识 69 3.1 组件及基本架构 70 3.1.1 简要回顾 70 3.1.2 特征 70 3.1.3 支持的硬件 71 3.2 内存管理 71 3.2.1 虚拟内存和分页 72 3.2.2 工作集 74 3.2.3 内核内存和用户内存 74 3.2.4 内核内存空间 75 3.2.5 区段对象 77 3.2.6 VAD树 78 3.2.7 用户模式的内存分配 78 3.2.8 内存管理API 79 3.3 对象与句柄 80 命名对象 81 3.4 进程与线程 83 3.4.1 进程 84 3.4.2 线程 84 3.4.3 运行状态切换 85 3.4.4 同步对象 86 3.4.5 进程初始化顺序 87 3.5 应用程序编程接口 88 3.5.1 Win32 API 88 3.5.2 本地API 90 3.5.3 系统调用机制 91 3.6 可执行文件格式 93 3.6.1 基本概念 93 3.6.2 映像区段(Image Sections) 95 3.6.3 区段对齐(Section Alignment) 95 3.6.4 动态链接库 96 3.6.5 部 97 3.6.6 导入与导出 99 3.6.7 目录 99 3.7 输入与输出 103 3.7.1 I/O系统 103 3.7.2 Win32子系统 104 3.8 结构化异常处理 105 3.9 结论 107 第4章 逆向工具 109 4.1 不同的逆向方法 110 4.1.1 离线代码分析 110 4.1.2 现场代码分析 110 4.2 反汇编器——ILDasm 110 4.3 调试器 116 4.3.1 用户模式调试器 118 4.3.2 内核模式调试器 122 4.4 反编译器 129 4.5 系统监控工具 129 4.6 修补工具 131 Hex Workshop 131 4.7 其他类型的逆向工具 133 可执行程序转储工具 133 4.8 结论 138 第2部分 应用逆向 第5章 未公开的技术 141 5.1 逆向和互操作性 142 5.2 基本原则 142 5.3 定位未公开的API函数 143 我们要找什么? 144 5.4 案例研究:NTDLL.DLL中的 5.4 Generic Table API 145 5.4.1 RtlInitializeGenericTable 146 5.4.2 RtlNumberGenericTableElements 151 5.4.3 RtlIsGenericTableEmpty 152 5.4.4 RtlGetElementGenericTable 153 5.4.5 RtlInsertElementGenericTable 168 5.4.6 RtlLookupElementGenericTable 1

21,497

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧