首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

为什么我们需要了解x86机器码

先来一句灵魂拷问:现在的编程语言都做得像吃饭睡觉一样简单了,为什么我们还要花功夫了解底层的x86机器代码?先别着急,我们先看看今天的内容。

下一次你可能会发现自己在调试汇编代码(对于我们中的某些人来说,可能唯一可用的调试方式就是汇编代码了),下面我列举了一些常见的机器代码以及它们”狡猾”的地方,供大家参考。

90

这是一个单字节的无操作(NOP)操作码。如果你希望给现有的代码打一些补丁,则可以无脑地使用这个操作码来替换现有代码。当然了,如果需要还原之前的版本,则你必须将旧版本的指令码替换回去。

CC

这是一个单字节的INT 3 操作码,它用来触发中断到调试器。

74/75

它们是JZ和JNZ的操作码。如果你希望反转一段代码流程,可以使用它们来进行互相替换。其他类似的组合有:72/73(JB/JNB),76/77(JBE/JA),7C/7D(JL/JGE)和7E/7F(JLE/JG)。你并不需要记住这些值,只需要明白,将这些位反转就会导致代码执行流的反转,如果需要撤销反转,则只需要还原这个位反转就可以了。

EB

这个是一个非条件性的短地址跳转(Unconditional Short Jump)指令。如果你希望将一个条件性跳转转换为一个非条件性跳转,则可以将74(假设)修改为EB。如果需要撤销,则你必须得记得之前的操作码字节。

00

换句话说,如果你想将一个条件性短地址跳转转换为一个直接跳转(Never-taken Jump),则你可以将指令的第二个字节修改为0。举个例子,”74 1C”就修改为”74 00″。这个跳转依然有效,只不过它仅仅是跳转到下一条指令,因此它没有任何有实际意义的效果。如果想撤销它,则需要记得修改之前的跳转偏移值。

B8/E8

这些操作码主要用于”MOV EAX, immed32″和”CALL”指令。我经常用它们来去除函数调用指令。如果你想跳过一些函数跳转,除了使用上面所说的将它们替换为90之外,还可以将E8修改为B8。如果想撤销它,则可以将B8修改为E8。

特别需要指出的是,这个方法只对于不包含有栈参数的函数,否则,如果你这样做的话,函数的栈就会遭到破坏。更一般地,你可以使用 83 C4 XX 90 90 (ADD ESP, XX; NOP; NOP) 其中 XX 是你需要弹出(pop)的字节数。 就我个人而言,我不记得这些指令的机器代码,所以我倾向于重写 CALL 指令,以便它在函数末尾调用”RETD”。

我更喜欢这些单字节补丁,而不是用90进行批量擦除,因为将代码恢复到之前的版本更加简单。

总结

了解事物的本质,有助于我们做出更加符合远期目标的决定。

当你的程序出现了预想不到的行为,别害怕,慢慢来:通过研究代码(底层汇编,甚至机器码),最终一定可以找出那只捣乱的老鼠。

在拓扑梅尔智慧办公平台(Topomel Box)的开发过程中,我也碰到过无数的Bug,首选方式,还是通过断点来一行一行地调试代码来定位问题。因为我对汇编语言了解不多,汇编这块应用就比较少,但也有一些涉猎,例如程序的反调试这块。

我相信,一切事物都有原因。

最后

Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。

本文来自:《Advantages of knowing your x86 machine code》

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20211223A04TT900?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券