Windows内核 基本汇编指令
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 ;恢复堆栈
Windows内核 基本汇编指令的更多相关文章
- 学习linux内核时常碰到的汇编指令(1)
转载:http://blog.sina.com.cn/s/blog_4be6adec01007xvg.html 80X86 汇编指令符号大全 +.-.*./∶算术运算符. &∶宏处理操作符. ...
- 学习windows内核书籍推荐 ----------转自http://tieshow.iteye.com/blog/1565926
虽然,多年java,正在java,看样子还得继续java.(IT小城,还是整java随意点)应用程序 运行于操作系统之上, 晓操作系统,方更晓应用程序. 主看windows,因为可玩性高,闭源才 ...
- Windows Kernel Way 1:Windows内核调试技术
掌握Windows内核调试技术是学习与研究Windows内核的基础,调试Windows内核的方式大致分为两种: (1)通过Windbg工具在Windows系统运行之初连接到Windows内核,连接成功 ...
- 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建
(原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...
- 【嵌入式开发】 ARM 汇编 (指令分类 | 伪指令 | 协处理器访问指令)
作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42408137 转载请著名出处 本博客相关文档下载 : -- AR ...
- Windows内核编程时的习惯与注意事项
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html 一.内核编程注意细节: 在头文件中使用的是 <ntddk.h ...
- Windows内核基础知识-5-调用门(32-Bit Call Gate)
Windows内核基础知识-5-调用门(32-Bit Call Gate) 调用门有一个关键的作用,就是用来提权.调用门其实就是一个段. 调用门: 这是段描述符的结构体,里面的s字段用来标记是代码段还 ...
- Windows内核中的CPU架构-6-中断门(32-Bit Interrupt Gate)
Windows内核中的CPU架构-6-中断门(32-Bit Interrupt Gate) 中断门和调用门类似,也是一种系统段.同样的它也可以用来提权. 中断门: 虽然中断门的段描述符如下: 但是中断 ...
- 【转载】64 位 Windows 内核虚拟地址空间布局(基于 X64 CPU)
原文链接:http://shayi1983.blog.51cto.com/4681835/1734822 本文为原创翻译,原文出处为 http://www.codemachine.com/articl ...
随机推荐
- psql-04数据类型(2)
复合类型 PostgreSQL中可以如C语言中的结构体一样定义一个复合类型; 创建 create type person as ( name text, age int, sex boolean ); ...
- http://www.cnblogs.com/younggun/archive/2013/07/16/3193800.html
http://www.cnblogs.com/younggun/archive/2013/07/16/3193800.html
- JavaScript 参考手册——javascript本地和内置对象、BOM、DOM
本部分提供完整的 JavaScript 参考手册: JavaScript 本地对象和内置对象 Browser 对象(BOM) HTML DOM 对象 JavaScript 对象参考手册 本参考手册描述 ...
- EF框架step by step(2)—Model-First
这一篇主要说一下EF框架中,Model First做法,仍然采用上一篇的案例.但增加评论功能.首先打开Blog.edmx文件,在空白处右键,添加新实体Comment,如下图示: 点击确定,关闭窗口. ...
- WPF在DLL中读取Resource的方法
WPF是个用户控件,被WinForm调用.而WinForm是在一个DLL类库中被调用.试了很多方法,都无法将Resource中的图读进程序.用下面的方法总算实现了. /根据图片的名称,从资源中找到 ...
- Windows下安装Docker
放在三年前,你不认识Docker情有可原,但如果现在你还这么说,不好意思,只能说明你OUT了,行动起来吧骚年,很可能你们公司或者你即将要去的公司,或者你想去的公司很可能就会引入Docker,或者已经引 ...
- Colorado Potato Beetle(CF的某道) & 鬼畜宽搜
题意: 一个人在一张大图上走,给你路径与起点,求他走出的矩形面积并.(大概这个意思自行百度标题... SOL: 与其说这是一道图论题不如说是一道生动活泼的STL-vector教学.... 离散化宽搜, ...
- android 百度地图 通过剪裁图片添加 Marker
初始化百度地图: private void initViews() { mMapView = (MapView) findViewById(R.id.bmapView); mBaiduMap = mM ...
- URAL 1427. SMS(DP+单调队列)
题目链接 我用的比较传统的办法...单调队列优化了一下,写的有点搓,不管怎样过了...两个单调队列,存两个东西,预处理一个标记数组存... #include <iostream> #inc ...
- 通过/etc/rc.local实现开机自动拉起服务
添加服务到/etc/rc.local 如自动拉起apache服务: /etc/rc.local: #!/bin/sh # # This script will be executed *after* ...