今天在写一个PE文件解析器的时候,大致写了这样一段代码:
本以为0X88没有超出%02X的格式限制。输出应该是88,殊不知输出的结果是FFFFFF88。正当我百思不得其解时,我修改了一下宏定义那句:
#define BYTE unsigned char
结果发现输出按照88正常输出。经过查找资料和反汇编分析,我得到了正确的结论,分析如下:
①%X默认的输出宽度为整型的宽度,在32位编译器中即4字节。这点验证可查看反汇编(VC++6.0):定义时虽然是byte ptr [ebp-4]只占用栈中一个字节,但是在输出时要按照4字节输出,则先将其用movsx带符号扩展为四个字节再输出。而由于定义时是一个有符号数,那么88H即10001000,符号位为1,则补齐四字节时前面全部补全为1,带符号扩展得到FFFFFF88,输出也为FFFFFF88。
②当我将#define BYTE char修改为#define BYTE unsigned char时,虽然也是扩展,但由于是一个无符号数,符号位为0,前面全部补0即0X00000088,在以%02X输出时为88。反汇编如下(虽然没有用不带符号扩展指令,但是and eax,0XFF的效果是一样的,都将高为全部置为0):
总结:
①%X默认按照int的宽度输出,%lx默认按照long的宽度输出(32位编译器下二者无差别);而%默认llx则是按照long long的宽度输出。
②不足默认宽度的会被宽展为指定宽度,并且扩展时依据符号进行扩展。
③%0NX,如果要输出值的字符个数大于N则“0N”的修饰无用,不要误认为输出的值占的字符个数和N有必然联系。
④基础知识的细节很重要,但是调试技术的掌握更为重要。