老码识途——1.1反汇编机器码入门

开始学习“老码识途”这本书,希望能有所收获。

目录

测试代码

反汇编

内存观察窗体

修改赋值语句机器码

直接构建新的赋值语句


 

计算机环境Win7 32位
调试环境VS2019 32位

 

 

 

测试代码

单纯在新建工程上加了全局变量

#include <iostream>
int gi;
int main()
{
    gi = 12;
    std::cout << "Hello World!\n";
}

VS快捷键:

F9加断点

F5断点调试

Ctrl+F5 非调试运行,断点无效

 

反汇编

开启位置:调试—窗口—反汇编

可代码处右键勾选“显示代码字节”,去除符号名,得到如下:

反汇编中的结构,以断点这一行为例:mov指令的地址,mov指令的机器码,汇编指令

此时可以检查 gi 的地址,选择调试—窗口—监视,输入 &gi

该地址与反汇编中的地址一致,只是反汇编中加了16进制的h符号;

我们可以拿出move指令的机器码来验证mov指令的相关信息

机器码:C7 05 3C C1 CD 00 0C 00 00 00

赋值的12转成16进制为:0C, 此时可以猜测为机器码的 0C可能是赋值12的意思;

gi变量的地址为:00CDC13C,此时可以拆分 00 CD C1 3C,此时对比可以发现实际
就是机器码中的 3C C1 CD 00,但是是倒过来写的;

而最前面的 C7 05 便是mov的指令,此时可以得到如下结论

C7 05(mov的指令) 3C C1 CD 00(gi变量的地址) 0C 00 00 00(代表值12)

这个机器码从右往左看,最后4个字节倒过来 00 00 00 0c,16进制转成10进制就是12,int类型4个字节,1个字节8位,即2个16进制数;

此时可以发现规律,机器码实际上是倒序的;

实际上这里可以引入一个知识点:

内存中存储整数的规范:

小端机(小端序):低字节存在低地址,高字节存高地址;

大端机(大端序):低字节存在高地址,高字节存低地址;

此时我们可以知道倒序属于小端机,即小端序(Intel x86系列CPU是小端机)

若是大端序,则12的机器码肯定是 00 00 00 0c;

 

内存观察窗体

位置:调试—窗口—内存

在地址栏中输入地址,其值为16进制,输入后下方显示内存面板区,显示的是内存的值左边是内存地址,地址从左到右,从上到下增加,显示单位为字节。

可以输入gi的地址

然后单步执行“gi = 12”;

此时出现了 0c,即赋值为12,但我们并不知道是否修改了4个字节还是只修改了1个字节,此时我们可以使用内存窗体的另一个功能——修改内存的值。

重新执行程序,但断点中断时,将指向的4字节全部修改为11,如下:

然后单步执行 gi = 12,看到内存变化为 0c 00 00 00,所以修改了4个字节。

 

同理,我们可以通过内存窗体来验证 mov指令是否存在反汇编第一列的地址中:

 

修改赋值语句机器码

我们是否可以通过修改内存中的值,从而达到修改指令的效果?

如:修改为: gi = 894567,计算gi的16进制得到:0xda667;

按照上面所说,我们是否可以把mov指令从

C7 05 3C C1 DA 00 0C 00 00 00,改为:

C7 05 3C C1 DA 00 67 a6 0d 00

这里的对应关系可以这么看:

0x 00 0d a6 67

倒序
0x 67 a6 0d 00

可以在VS中修改,步骤:断点到 gi=12,修改mov的内存值

修改完后,按F10单步执行,查看监视窗口的值变化,发现确实是894567;

 

直接构建新的赋值语句

通过上面,我们能否可以抛开C、C++代码,自己构造指令来执行?

实际上,我们可以查看当前寄存器的值,此时需要激活寄存器窗体。(位置:调试—窗口—寄存器)

 

再次断点调试,此时我们可以看到反汇编显示的指令地址是否与EIP寄存器中的值相同。

(EIP寄存器为32位,该寄存器指向哪里,CPU就将该地址作为将执行指令的入口,即使错误的指向了数据区)

 

按F10单步执行,发现下一条指令的地址也是EIP寄存器中的值。

假定我们构造了一段指令,则需要用jmp语句跳转这段指令,执行完后,还需要跳转回来如下,否则会导致不可预料的行为。

因此在构造的新代码中需要构造jmp指令,操作码为 ff 25;

其中C语言程序嵌入汇编方式

载入汇编两种方式:
1._asm{...},在花括号内填写多条汇编语句;
2._asm一条汇编语句,如 “_asm mov eax,1”

看书中例子:

//写机器指令代码
void* buildCode() {

}

int gi;
void main() {
	void* code = buildCode;
	_asm {
		mov address, offset _lb1 //将标签_lb1即下面的_lb1:后语句的地址设定给变量address
        //即printf("02 gi = %d\n", gi);语句的地址
	}
	gi = 12;
	printf("01 gi = %d\n", gi);
	_asm jmp code //执行自己构建的代码
	gi = 13;

_lb1:
	printf("02 gi = %d\n", gi);
	getchar();
}

继续分析 buildCode函数,它要构建的汇编指令如下

mov gi,18;
jmp address

这两条指令的机器码如下,应需要分配16个字节:

引用书中的代码和对应关系,可以很好看出写法

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值