魔幻差异
0x01 said it in front
实验环境
win11
x86_64
(一开始用mac启动pd用win虚拟机自带的x86仿真,在这个环境上进行调试,但是遇到很奇怪的调试问题,什么调试工具都不管用包括x32dbg windbg ida pro,遂放弃)
实验工具
windbg
idapro
010editor
0x02 find differences
两个程序a1.exe和a2.exe bindiff一下很像,没有差别很大的地方,similarity都是1 这两个程序在汇编层面上的指令都是一样的,需要深一步找不同(pe结构)
简单写个脚本找一下内存上的不同
1
2
3
4
5
6
7
8
9
with open('A1.exe', 'rb') as f1:
data1 = f1.read()
with open('A2.exe', 'rb') as f2:
data2 = f2.read()
for i in range(len(data1)):
if data1[i] != data2[i]:
print(hex(i),data1[i],data2[i])
脚本结果表明在文件0x9a,0x9b内存地址上的不同
1
2
0x9a 6 2
0x9b 0 24
打开010editor看看是哪个数据结构的不同 可以看到两个程序的链接器版本不同,a1.exe使用的6.0,a2.exe使用的2.24 

这就是两个程序产生的输出不同的缘由
0x02 a1.exe调试与a2.exe调试
使用windbg调试a1.exe
1
ba w4 the_addr_%n_var
ba断点发现在msvcrt_output_1+0x704处对这个变量进行了修改(10 -> 15)
下面还原从printf到msvcrt_output_1+0x704处都经过了哪些函数,长度是怎样一步步算出来的
- 跟进到第一个printf
- 继续跟进到printf源码中的msvcrt_output_1
- msvcrt_output_1: write_string [esp-220h] -> 0xe
- msvcrt_output_1: write_char [esp-220h] -> 0xf
- msvcrt_output_1: __get_printf_output_count 检查是否允许修改 检查成功
- 长度修改成功
a2.exe同理
- 跟进到第一个printf
- 继续跟进到printf源码中的msvcrt_output_1
- msvcrt_output_1: write_string [esp-220h] -> 0xe
- msvcrt_output_1: write_char [esp-220h] -> 0xf
- msvcrt_output_1: __get_printf_output_count 检查是否允许修改 检查失败
- 长度修改失败
0x03 msvcrt.dll 逆向
所以是 __get_printf_output_count的原因 逆向一下msvcrt.dll 看看这个函数干了啥 
大概就是将内存里的一个值和(__security_cookie | 1)相比较 相等则可以修改成功 不相等则跳转errno
0x04 why different
所以为什么a1.exe可以判定想等 a2.exe则判定不想等 进入__get_printf_output_count动态调试一下
可以看到[7704b9cch] == ecx
a2.exe如下 
可以看到[7704b9cch] = 0 所以导致检查失败
0x05 [7704b9cch]何时被写入
1
ba w4 7704b9cc
发现是在__set_printf_output_count处修改了 
__set_printf_output_count在__core_crt_dll_init被调用
深入跟进__core_crt_dll_init找到调用逻辑
只有链接器版本6.0 才可以支持开启%n 支持
而我们的a1.exe的链接器版本是6.0
破案了
0x06 总结
两个程序的汇编指令上没有任何差别,在PE结构上是NTheader中OptionHeader中的链接器主次版本的不同,通过逐步深入printf的实现,发现两个程序在__set_printf_output_count处的调用不同,a1.exe调用了该函数因此获取了%n的支持,a2.exe没有调用该函数,因此没有%n支持走了invalid parameter那一套流程,究其原因就是在__core_crt_dll_init中会判断链接器版本号只有6.0支持%n,会调用__set_printf_output_count,开启%n支持。 而由前面提到的a1.exe的链接器版本号就是6.0,这就是原因所在。
分析完毕, 感谢阅读。

