除了那些经过加密存储的字符串以外,我们可以使用任何一款的hex editor直接编辑C字符串。即使那些不了解机器码和可执行文件具体格式的人,也可以使用这项技术直接编辑可执行程序中的字符串。修改后的字符串,其长度不得大于原来字符串的长度,否则可能覆盖其他的数据甚至是其他指令。在MS-DOS盛行的时代,人们普遍使用这种方式直接用译文替换软件中的外文文字。至少在20世纪80年代和90年代的前苏联,这种技术十分流行。所以,那个时代也出现了各种古灵精怪的超短缩写:预定长度的字符串存储空间可能容纳不下完整的译文,所以软件翻译人员不得不绞尽脑汁压缩译文的长度。
在修改Delphi程序的字符串时,有时还要调整字符串的长度。
修改可执行文件中汇编指令的方式有以下几种:
禁用某些指令。此时只要使用0x90(NOP)替换相应的汇编指令即可。
禁用条件转移指令。在修改74 xx(JZ)这样的条件转移指令时,我们可以直接把转移指令的2个字节替换为两个0x90(NOP),也可以把第二个字节(jump offset)替换为0,即把偏移量固定为0。
强制程序进行跳转。有些时候,我们需要把条件转移指令替换为跳转指令,强制其进行跳转。此时,把opcde的第一个字节替换为JMP的0xEB即可。
禁用某函数。只要把函数的第一个指令替换为RETN(0xC3),那么它就不会运行。只要程序的调用约定不是stdcall(第64章第2节),那么这种修改方法都不会有问题。在修改遵循stdcall约定的函数时,修改人员首先要注意函数参数的数量(可查阅原函数的RETN 指令),然后使用带有16位参数的RETN(0xC2)指令替换函数的第一条指令。
某些情况下,被禁用的函数必须返回0或1。此时可使用“MOV EAX,0”或“MOV EAX,1”进行处理。直接使用这两条指令的opcode进行替换时,您会发现其opcode较长。这种情况下就可以使用“XOR EAX,EAX”(0x31 0xC0 两个字节)或XOR EAX,EAX /INC EAX (0x31 0xC0 0x40三个字节)进行替换。
很多软件采用了防范修改的技术。这种功能通常都由“读取可执行文件代码”和“校验和(checksum)检验”两个步骤分步实现。即是说,要实现防修改机制,程序首先要读取(加载到内存里的)程序文件。我们可设置断点,解析其读取内存函数的具体地址。
tracer工具可以满足这种调试需求。它具有BPM(内存断点)功能。
在修改程序时,不得修改PE可执行文件的relocs(参见本书的68.2.6节)。Windows的加载程序会使用新的代码覆盖这部分代码。如果使用Hiew打开可执行程序,会发现这部分代码以灰色显示(请参考图7.12)。万不得已的时候,您可使用跳转指令绕过reclos,否则就要编辑relocs的数据表。