硬编码逆向分析——硬编码概述和前缀指令
硬编码
硬编码概述
计算机只认识0和1,也就是二进制,任何一个可执行程序最终就是0和1组成,如果非要细分的化,可以分为两个部分:数据和指令,但是对于这两个部分没有明显的界限,如果你把某个地址给到CPU的EIP寄存器,这时候这个地址就会当成指令去执行,但是指令是有自己的格式,如果你指向的地址没有按照指令的格式来,就会执行出错,换句话说,如果你指向的地址是一段数据,但是这个数据格式也是按照指令格式的,那么也就会去执行。
指令的格式取决于你的CPU类型:X86、X64、Arm...而所谓的硬编码就是机器码、指令,本章节主要讲解的格式就是X86的指令格式。
如下图所示,是两个软件中打开着某个可执行文件(左 DTDebug,右 IDA),我们可以看见用红色方框,标记的部分就是硬编码,其右边是汇编指令,但实际上你所看见的这些汇编指令都是这些软件的反汇编引擎帮你从硬编码转换而来的(可执行文件并不会有这些东西,都是0和1)。
所有与计算机底层相关的行业都需要深入学习、了解硬编码,例如病毒行业的ShellCode,反病毒行业的特征码,加密与破解行业的指令壳、VMP,外挂行业的HOOK以及反外挂行业的提串都离不开硬编码的学习。
前缀指令
如下图所示就是硬编码的结构,其有6个部分,我们现在所要了解的就是前缀指令,也就是第一部分。
我们可以在DTDebug中找到前缀指令,如下图所示在硬编码的区域在前缀指令之后有一个":"冒号,这是为了方便使用者使用:
前缀指令分组
上文中我们了解到硬编码的结构,在结构图中第一部分为前缀指令,前缀指令下面有一个描述是“最多有四个前缀,每个前缀只能有1个字节”(并且途中表示前缀指令是可选的,所以前缀指令最少占0字节,最多占4字节),这也就表示这前缀指令是有分组的,也就是四组,在前缀指令中每组只能出现一个,分组如下所示:
-
LOCK和REPEAT前缀指令:
-
LOCK(硬编码:F0):LOCK是用来锁地址总线,例如01002891这个地址开头是LOCK指令,当前地址在多核情况下,只能有一个核CPU去读这个地址,其他核CPU是可以不读取的;
-
REPNE/REPNZ(硬编码:F2):REP*就是我们之前汇编所学过的重复指令,在这里的NE或者NZ表示根据EFLAG标志寄存器ZF位为0的时候执行;
-
REP/REPZ(硬编码:F3);重复指令,在这里Z表示根据EFLAG标志寄存器ZF位为1的时候执行。
-
-
段前缀指令
-
这里的段实际上表示着段寄存器,如下英文字母都表示是寄存器:
-
CS(硬编码:2E)
-
SS(硬编码:36)
-
DS(硬编码:3E)
-
ES(硬编码:26)
-
FS(硬编码:64)
-
GS(硬编码:65)
-
-
-
-
如下图所示为DTDebug中的段寄存器位置:
-
-
-
默认情况下你要使用段寄存器的地址不加段前缀指令时都为DS段寄存器的地址:
-
-
-
如果你想使用其他段寄存器就可以在这段硬编码之前加入对应段前缀指令(在DTDebug中选中需要修改的然后按快捷键Ctrl+E进行修改):
-
注意:是在ff15之前加的65表示改变段寄存器!
-
操作数宽度前缀指令,硬编码:66,其是用来改变操作数的宽度,这种改变是双向的,例如你当前操作数的宽度是32位的,当你在硬编码之前加上66,则操作数的宽度变成16位,反之如果你当前操作数的宽度是16位的,加上之后就会变成32位。
-
例如在如下的程序中有一个硬编码55,对应的汇编就是PUSH EBP,这个EBP的宽度是32位的,这里的宽度之所以是32位是因为当前CPU的模式是32位的,想要知道当前CPU模式就需要涉及段寄存器的知识,这里我们简单了解下(后续课程深入了解),在段寄存器CS中有一个属性位称之为DB位,当DB位为1的时候当前CPU处于32位模式,为0的时候就表示当前CPU处于16位模式,所以在这里是32位的模式操作数也就是32为宽度。
-
-
-
那如果你在32位的模式下去使用16位的寄存器,就可以在硬编码之前加上66:
-
ODB里实验下:注意冒号其实是不体现的。
-
地址宽度前缀指令,硬编码:67,其用来改变地址宽度,你可以与操作数宽度前缀指令一样去理解(双向改变),例如在32位模式下寻址默认是32位的地址宽度,但是当你在硬编码之前加上67之后,寻址就按照16位的地址宽度进行了:
注意:前缀指令使用的时候是没有顺序的。