1)用VS2010新建Win32 Console Application,工程名为ACECore,工程建立完成后得到打开文件ACECore.cpp,代码如下:

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])

{

return 0;

}

2)用VS2010查看汇编代码的方法:

1. VC必须处于debug状态才能看到汇编指令窗口。因此在上面代码return 0一句上设置断点。

2.按下F5键调试程序,当程序停在断点处时,打开菜单“Debug”下的“Windows”子菜单,选择“Disassembly”。这样就出现反汇编窗口,显示汇编代码:

--- g:/acecore/acecore/acecore.cpp ---------------------------------------------

// ACECore.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])

{

00411350  push        ebp

00411351  mov         ebp,esp

00411353  sub         esp,0C0h

00411359  push        ebx

0041135A  push        esi

0041135B  push        edi

0041135C  lea         edi,[ebp-0C0h]

00411362  mov         ecx,30h

00411367  mov         eax,0CCCCCCCCh

0041136C  rep stos    dword ptr es:[edi]

return 0;

0041136E  xor         eax,eax

}

3)相关汇编指令:

push:把一个32位的操作数压入堆栈中,这个操作导致esp被减4。esp被称为栈顶,压入堆栈的数据越多,这个堆栈也就越堆越高,esp地址就越来越小。在32位平台上,esp每次减少4(字节)。

pop:esp被加4,一个数据出栈。pop的参数一般是一个寄存器,栈顶数据被弹出到这个寄存器中。

某些指令会“自动”地操作堆栈:call指令会把它的下一条指令的地址压入堆栈中,然后跳转到它调用的函数的开头处;而单纯的jmp是不会这样做的。同时,ret会自动弹出返回地址。

call的本质相当于push+jmp,ret的本质相当于pop+jmp。

不但push、pop、call和ret会操作堆栈,sub和add也可以用于操作堆栈。如果要一次在堆栈中分配4个4字节长整型的空间,那么没有必要4次调用push,很简单地把esp减去4*4=16即可。当然,也可以同样地用add指令来恢复它。

lea:取得地址(第二个参数)后放入到前面的寄存器(第一个参数)中。实际上,有时候lea用来做和mov同样的事情,比如赋值:

lea edi,[ebp – 0cch]

其中,方括弧表示存储器,也就是ebp-0cch这个地址所指的存储器内容。但是lea语法要求取[ebp-0cch]的地址,这个地址就是ebp-0cch,把这个地址放到edi中,也就是说,这等同于:

mov edi,ebp-0cch

但以上的mov指令时错误的,因为mov不支持后一个操作数写成寄存器减去数字。而lea支持,因此可以用lea来代替它。

Stos指令:

mov ecx, 30h

mov eax,0CCCCCCCCh

rep stos dword ptr es:[edi]

stos是串存储指令,它的功能是将eax中的数据放入edi所指的地址中,同时,edi会增加4(字节数)。rep时指令重复执行ecx中填写的次数。方括弧表示存储器,这个地址实际上就是edi的内容所指向的地址。这里的stos其实对应的是stosd,其他还有stosb、stosw,分别对应于处理4、1、2个字节。

上面代码中对堆栈30h*4(=0c0h)个字节初始化为0CCh(也就是int3指令的机器码),这样发生意外时执行堆栈里面的内容会引发调试中断。

==============lost的分割线===============

C函数的参数传递过程:

1)C语言程序通过堆栈把参数从函数外部传入到函数内部,同时,在堆栈中划分区域来容纳函数的内部变量。对于C语言默认的调用方式,函数调用方把参数反序(从右到左)地压入堆栈中,被调用方把堆栈复原。这些参数对齐到机器字长,16位、32位、64位CPU下分别对齐到2、4、8个字节。

2)函数调用规则指的是调用者和被调用者函数间传递参数及返回参数的方法,在Windows上,常用的有Pascal方式、WINAPI方式(_stdcall)、C方式(_cdecl)。

_cdecl调用规则:

(1)参数从右到左进入堆栈;

(2)在函数返回后,调用者要负责清除堆栈,所以这个调用常会生成较大的可执行文件。

_stdcall又称为WINAPI,其调用规则:

(1)参数从右到左入栈;

(2)被调用的函数在返回前自行清理堆栈,所以生成的代码比cdecl下。

Pascal调用规则:

(1)参数从左到右入栈;

(2)被调用参数在返回前自行清理堆栈;

(3)不支持可变参数的函数调用。

此外,在Windows内核中还常见有快速调用方式(_fastcall);在C++编译的代码中有this call方式(_thiscall)。

3)以如下函数作为例子分析:

void ACEFunction(int a, int b)

{

int c = a + b;

}

int _tmain(int argc, _TCHAR* argv[])

{

int a = 1;

int b = 2;

ACEFunction(a, b);

return 0;

}

标准的C函数调用方式(_cdecl):

(1)调用者把参数反序地压入堆栈中;

(2)调用函数;

(3)调用者负责把堆栈清理复原。

注意:在Windows中,不管哪种调用方式都是返回值放在eax中,然后返回。外部从eax中得到返回值。

_cdecl方式下被调用函数需要做以下的事情:

(1)保存ebp。ebp总是被我们用来保存这个函数执行之前的esp的值,执行完毕后,我们用ebp恢复esp;同时,调用此函数的上层函数也用ebp做同样的事情,所以先把ebp压入堆栈,函数返回之前弹出,避免ebp被我们改动。

(2)保存esp到ebp中。

上面两步的代码如下:

;保存ebp,并把esp放入ebp中,此时ebp和esp同

;都是这次函数调用时的栈顶

00411360  push        ebp

00411361  mov         ebp,esp

(3)在堆栈中腾出一个区域用来保存局部变量,这就是常说的所谓局部变量时保存在栈空间中的。方法是:把esp减少一个数值,这样就等于压入了一堆变量。恢复时,只有把esp恢复成ebp中保存的数据就行。

(4)保存ebx、esi、edi到堆栈中,函数调用完后恢复。

上面两步对应代码如下:

;把esp往下移动一个范围,等于在堆栈中放出一片新

;的空间来保存局部变量

00411363  sub         esp,0CCh

00411369  push        ebx

0041136A  push        esi

0041136B  push        edi

(5)把局部变量区域初始化成全0CCCCCCCCh。0CCh实际上是int3指令的机器码,这是一个断点中断指令。因为局部变量不可能被执行,如果执行了,必然程序出错,这时发生中断来提示开发者。这时VC编译Debug版本的特有操作:

0041136C  lea         edi,[ebp-0CCh]

00411372  mov         ecx,33h

00411377  mov         eax,0CCCCCCCCh

0041137C  rep stos    dword ptr es:[edi]

(6)然后做函数里应该做的事情。参数的获取是ebp+12字节为第二个参数,ebp+8字节为第一个参数(反序入栈),依次增加。最后ebp+4字节处是要返回的地址。

(7)恢复ebx、esi、edi、esp、ebp,最后返回:

00411387  pop         edi

00411388  pop         esi

00411389  pop         ebx

0041138A  mov         esp,ebp

0041138C  pop         ebp

0041138D  ret

用VS2010编译Debug版本,完整的反汇编代码如下:

--- g:/acecore/acecore/acecore.cpp ---------------------------------------------

// ACECore.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

void ACEFunction(int a, int b)

{

00411360  push   ebp  ;保存ebp,并把esp放入ebp中。此时ebp与esp相同

00411361  mov   ebp,esp  ;都是这次函数调用时的栈顶

00411363  sub   esp,0CCh  ;把esp往上移动一个范围,等于在堆栈中放出一片新

;的空间用来存储局部变量

00411369  push   ebx  ;下面保存三个寄存器:ebx、esi、edi

0041136A  push   esi

0041136B  push   edi

0041136C  lea   edi,[ebp-0CCh] ;原本是想使用“mov edi, ebp-0CCh”,但是mov不支持

;“-”操作,所以先对ebp-0CCh取内容(即[ebp-0CCh]),

;再利用lea把[ebp-0CCh]的地址也就是ebp-0CCh放到edi中

;目的是把保存局部变量的区域(即ebp-0CCh开始的区域)

;初始化成全部为0CCCCCCCCh。

00411372  mov   ecx,33h

00411377  mov   eax,0CCCCCCCCh

0041137C  rep stos    dword ptr es:[edi]  ;写入0CCh指令(中断)

int c = a + b;

0041137E  mov   eax,dword ptr [a]  ;加法操作,从堆栈中取得从外部传入的参数。

00411381  add   eax,dword ptr [b]  ;通过ida反汇编可以看到,其实这两天指令是:

;mov eax, [ebp+8]

;add eax, [ebp+0Ch]

;参数是通过ebp从堆栈中取得的。这里看到的是

;VC调试器的显示结果,是为了方便阅读,

;直接加上了参数名

00411384  mov  dword ptr [c],eax

}

00411387  pop         edi  ;恢复edi、dsi、ebx

00411388  pop         esi

00411389  pop         ebx

0041138A  mov         esp,ebp  ;恢复原来的ebp和esp,让上一级调用的

0041138C  pop         ebp  ; 函数可以正常使用

0041138D  ret

主程序中对这个函数的调用方式是:

004123DC  mov         eax,dword ptr [b]  ;把b, a两个参数压入堆栈

004123DF  push        eax

004123E0  mov         ecx,dword ptr [a]

004123E3  push        ecx

004123E4  call        ACEFunction (411014h)  ;调用函数ACEFunction

004123E9  add         esp,8  ;恢复堆栈

转载于:https://www.cnblogs.com/alsofly/p/3623873.html

Windows内核 基本汇编指令相关推荐

  1. Linux操作系统汇编指令入门级整理知识点

    前言 我们大都是被高级语言惯坏了的一代,源源不断的新特性正在逐步添加到各类高级语言之中,汇编作为最接近机器指令的低级语言,已经很少被直接拿来写程序了,不过我还真的遇到了一个,那是之前的一个同事,因为在 ...

  2. 汇编指令入门级整理 | 原力计划

    作者 | AlbertS 出品 | CSDN 博客 前言 我们大都是被高级语言惯坏了的一代,源源不断的新特性正在逐步添加到各类高级语言之中,汇编作为最接近机器指令的低级语言,已经很少被直接拿来写程序了 ...

  3. windows debug下验证汇编指令中的mov指令传送数据

    一.知识储备 1.8086CPU是16位结构,有16根数据线,20根地址线.16根数据线可以一次性传送16位数据,也就是两个字节(也就是一个字)的数据. 什么是字? 在这个特定计算机中,字是其用来一次 ...

  4. 传统 51 与STC-Y5内核 51 单片机对比汇编指令

    传统 51 与STC-Y5内核 51 单片机对比&汇编指令 算术类指令 24 汇编助记符 功能说明 字节数 传统8051单片机所需时钟 STC 8051单片机所需时钟 ADD A,Rn 寄存器 ...

  5. 【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)

    作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42408137  转载请著名出处 本博客相关文档下载 :  -- A ...

  6. windows 内核情景分析

    原文很长:先转部分过来,有时间看一下: 一 windows 内核情景分析---说明 说明 本文结合<Windows内核情景分析>(毛德操著).<软件调试>(张银奎著).< ...

  7. Windows内核符号表学习总结

    内核符号表 http://blog.csdn.net/vbsourcecode/article/details/8555796 在进行Windows Driver开发调试中,内核符号表是个问题.由于网 ...

  8. Windows内核的表学习总结

    SSDT - 系统服务描述符表 SSDT(System Services Descriptor Table),系统服务描述符表.这个表就是一个把ring3的Win32 API和ring0的内核函数联系 ...

  9. Windows内核实验005 Inline Hook

    文章目录 准备工作 寻找Inline Hook的返回地址 编写代码 动态变化的返回地址 JmpTargetAddr Inline Hook基本框架 示例代码 实战HOOK KiTrap01 无需计算偏 ...

最新文章

  1. python填写excel-Python向excel中写入数据的方法
  2. unittest单元测试框架总结
  3. JavaScript基础:(加号,数值转换,布尔转换)
  4. linux / 终端常用快捷键
  5. 在servlet中读取初始化参数和上下文参数的方法getServletContext()和getInitParameter()
  6. 论文浅尝 | Convolutional 2D knowledge graph embedding
  7. 为什么大学打印店老板多是湖南人?
  8. mpu9250姿态融合算法_基于投票方式的机器人装配姿态估计
  9. swing程序 过时拉嘛_从关闭或过时的应用程序导入文件
  10. linux命令kill_什么是Linux中的kill命令?
  11. Layui第三方扩展LAY_EXCEL自定义导出数据类型
  12. 【问题记录】Win10笔记本电脑禁用自带键盘的方法
  13. 数据库的容灾与备份,你是如何处理的?
  14. 优酷视频kux格式转换为MP4的两种技巧
  15. 【转】Keil、uVision、RealView、MDK、Keil C51之间的区别比较
  16. 百度识图上线,体验以图搜图
  17. python自学笔记15之实例之绘图、dataframe操作、读写csv,excle
  18. CSDN的markdown编辑器的语法:字体的样式、大小、颜色怎么调节?建议收藏,让你的博客更漂亮
  19. C#模拟百度登录并到指定网站评论回帖(一)
  20. 最全微信产品相关文章

热门文章

  1. 某CrackMe算法分析
  2. java实现qq_java实现的类似qq聊天系统
  3. DPDK pmd驱动初始化(十九)
  4. leetcode算法题--石子游戏
  5. leetcode算法题--最长的斐波那契子序列的长度
  6. 根据id去重_Person Re-ID研究综述
  7. 阻塞队列只有一个线程在同一时刻对其进行或者读或者写
  8. [转]定位占用oracle数据库cpu过高的sql
  9. Atitit.upnp SSDP 查找nas的原理与实现java php c#.net c++
  10. PS摩棒工具如何选中自己想要的区域