汇编3栈帧,参数传递,串操作,混合汇编,x64,asm文件
基础知识2
选择结构
通过判断 + 条件跳转指令来实现
循环结构
通过判断 + 条件跳转指令来实现(会有一个向上跳转的语句)
函数调用约定
C调用约定: 由外部平衡栈
标准调用约定 : 由函数内部平衡栈
对象调用约定 : 由函数内部平衡栈, 寄存器用于保存对象的首地址(就是this指针)
快速调用约定 : 由函数内部平衡栈(传参都是从右往左传递.), 用到 ecx , edx 来依次传递前两个参数.
通过 call 指令, call指令的原理是: 将call指令的下一条指令的地址压入栈中, 然后再进行跳转.
通过 ret 指令, 来结束函数的调用,回到调用点, 原理: 将保存在栈中的返回地址 pop 到 eip .
需要在函数内部访问形参的方式:
通过 esp 来访问, 缺点: esp寄存器是受到一些栈操作指令被改变的(例如:push,call,ret) , 当它被改变之后, 就不能通过一个固定的偏移来定位栈里面的参数了.
通过 ebp 来访问的 , 原理: 进入到函数内部时, 将esp的值保存到ebp, 然后ebp是不会受到栈操作指令的映 像, 可以使用一个固定的偏移来定位栈里面的参数.
函数编写的要求:
保证在函数内部不会修改寄存器的值, 如果要修改,需要保证离开函数之后, 将修改的寄存器的值恢复 回去.
保证离开函数之后, 栈是平衡的(进入到函数时,栈顶esp指向了哪里, 那么在离开函数之后,esp也必须还 指向了那里),ebp也一样.
在函数内部如果要使用局部变量.
打开函数栈帧通过 push ebp; mov ebp,esp;
开辟局部变量占用的栈空间:
通过 ebp-4 来定位在栈中的局部变量: ebp-4 是局部变量1, ebp-8 是局部变量2
int fun( )
{
int nArr[]={1,2,3,4,5};
printf("%d",nArr[0]);
}
fun proc
push ebp ;\
; 打开栈帧
mov ebp , esp;/
sub esp , 20 ; 开辟局部变量的栈空间
mov [ebp-4] , 1; 初始化局部变量
mov [ebp-8] , 2; 初始化局部变量
mov [ebp-0ch] , 3; 初始化局部变量
mov [ebp-010h] , 4; 初始化局部变量
mov [ebp-014h] , 5; 初始化局部变量
mov esp , ebp; 恢复局部变量占用的栈空间
pop ebp ; 恢复栈帧 ret fun endp
5.字符串的汇编
char* pStr = "abcdefg";
dword ptr [ebp-18h],0BA7B40h
//dword ptr [pStr],offset string "abcdefg" (0BA7B40h)
在内存输入00BA7B40
6.指针
//函数
void fun2(char* p) {
p = "123456";
}
//调用时
fun2(pStr);
00BA19EC 8B 45 E8 mov eax,dword ptr [pStr]
00BA19EF 50 push eax
00BA19F0 E8 4F F8 FF FF call fun2 (0BA1244h)
00BA19F5 83 C4 04 add esp,4
//进函数 p = "123456";
00BA17D8 mov dword ptr [ebp+8],0BA7B30h
//00BA17D8 mov dword ptr [p],offset string "123456" (0BA7B30h)
7.二级指针
//函数
void fun3(char** pp, char*& pr) {
*pp = "123456";
pr = "456789";
}
//调用时
fun3(&pStr, pStr);
00BA19F8 8D 45 E8 lea eax,[pStr]
00BA19FB 50 push eax
00BA19FC 8D 4D E8 lea ecx,[pStr]
00BA19FF 51 push ecx
00BA1A00 E8 02 F9 FF FF call fun3 (0BA1307h)
00BA1A05 83 C4 08 add esp,8
//进函数
*pp = "123456";
00BA1838 8B mov eax,dword ptr [ebp+8]
//00BA1838 8B mov eax,dword ptr [pp]
00BA183B C7 mov dword ptr [eax],0BA7B30h
//00BA183B mov dword ptr [eax],offset string "123456" (0BA7B30h)
pr = "456789";
00BA1841 mov eax,dword ptr [ebp+0Ch]
//00BA1841 8B mov eax,dword ptr [pr]
00BA1844 C7 mov dword ptr [eax],0BA7B38h
00BA1844 C7 mov dword ptr [eax],offset string "456789" (0BA7B38h)
8.指针 引用 值传递
//函数
void fun2(int n, int* p, int& r) {
n = 10;
*p = 10;
r = 10;
}
//调用时,先传最右边的
fun2(n, &n, n);
//int&
00BA1A0F 8D 45 DC lea eax,[ebp-24h]
00BA1A12 50 push eax
//int*
00BA1A13 8D 4D DC lea ecx,[ebp-24h]
00BA1A16 51 push ecx
//int
00BA1A17 8B 55 DC mov edx,dword ptr [ebp-24h]
00BA1A1A 52 push edx
//进入函数
00BA1A1B E8 29 F8 FF FF call 00BA1249
//C调用
00BA1A20 83 C4 0C add esp,0Ch
//进函数
n = 10;
00BA1768 mov dword ptr [ebp+8],0Ah
//00BA1768 mov dword ptr [n],0Ah
*p = 10;
00BA176F mov eax,dword ptr [ebp+0Ch]
00BA1772 C7 mov dword ptr [eax],0Ah
//00BA176F mov eax,dword ptr [p]
//00BA1772 mov dword ptr [eax],0Ah
r = 10;
00BA1778 mov eax,dword ptr [ebp+10h]
00BA177B mov dword ptr [eax],0Ah
//00BA1778 mov eax,dword ptr [r]
//00BA1778 mov dword ptr [eax],0Ah
9.强转
char* p = (char*)&n;
*p = 0;
*(int*)p = 0;
p = (char*)0;
char* p = (char*)&n;
lea eax,[n]
mov dword ptr [p],eax
*p = 0;
mov eax,dword ptr [p]
mov byte ptr [eax],0
*(int*)p = 0;
mov eax,dword ptr [p]
mov dword ptr [eax],0
p = (char*)0;
mov dword ptr [p],0
串操作指令
rep 重复操作前缀
默认将重复的次数保存在ecx中. 每重复一次,就递减ecx的值. 当ecx的值等于0时,就不再重复.
不能单独使用 , 也不能和任意的指令搭配使用,只能和串操作指令结合使用,例如:
mov ecx,10
rep inc eax; // 错误
stos
默认操作数是 edi 和 al/ax/eax ,
Q功能 : 将 al/ax/eax 的值填充到edi指向的内存中. 填充完之后, 会自增/自减(取决于 DF 方向标志) edi 的值 , edi的值在自增的时候有(1/2/4)的可能,实际增加多少,取决于指令的后缀, 它的后缀 stosb , stosw,
和 rep 指令结合使用之后,功能类似于 memset
lods
默认操作数: esi 和 eax
功能: 将esi指向的内存的数据保存到eax中.
movs
默认操作数 edi ,
功能 : 将esi指向的内存的数据保存到 edi 中 和 rep 结合之后, 功能类似 memcpy
cmps
默认操作数是edi,esi
功能: 将esi和edi指向的内存进行比较, 将比较的结果设置到eflags寄存器中. 和 repe 结合,功能类似 memcmp
scas
默认操作数:
功能 : 使用edi指向的字节和eax进行比较, 将比较的结果设置到标志寄存器中 和 repne 结合,功能类似 strlen
int main(){
char buff[100];
char buff2[] = "hello world";
// 1. 将buff全部填充为0(memset(buff,0,100))
_asm
{
lea edi,[buff]; mov ecx,100; mov al ,0;
rep stosb;
}
// 2. 将buff2的内容拷贝到buff中(memcpy(buff,buu2,11)
_asm {
lea esi, [buff2]; lea edi, [buff]; mov ecx, 11;
rep movsb;
}
// 3. 比较buff2和buff的内存是否一样(memcmp(buff2,buff)) int nCmpFlag = 0;
_asm
{
lea esi, [buff2]; lea edi, [buff]; mov ecx, 11;
repe cmpsb;
mov [nCmpFlag ],ecx;
}
if (nCmpFlag == 0) { printf("内存相等\n");
}
else {
printf("内存不相等\n");
}
// 4. 计算buff2字符串长度(strlen(buff2))
_asm
{
lea edi,[buff2];
mov al,0;
mov ecx,0xFFFFFFFF;
repne scasb;
not ecx; //按位取反得到原码
dec ecx; // ecx存的是补码,-1获取反码
}
混合编程
c和汇编一起出现在同一个源文件中
内嵌汇编
通过 _asm 关键字来实现
单行内联汇编
int main()
{
int n;
_asm mov [n] , 10
}
块状内联汇编
int main()
{
_asm {
mov eax,100;
add eax, 20;
}
}
在内联汇编中定义一个字节的机器码
int main()
{
_asm
{
jmp _FLAG
_emit 0xe9; 直接在此处定义1个字节的机器码,
;初始值是0xe9 _FLAG:
}
}
使用asm文件
在64位程序中,不能使用 _asm 关键字来使用内联汇编了.
给项目添加一个宏汇编编译器.
添加一个.asm的文件
指定这个asm的文件使用汇编编译器来编译(否则默认是不编译的)
在asm中定义汇编代码及结构体
.model flat,stdcall
.data
; 声明结构体的原型
MyStruct struct
cChar db ?
nNum dd ?
MyStruct ends
.code
;
; 如果在汇编的函数声明中加上了
; 参数的声明,汇编编译器会自动
; 加上打开栈帧的代码.
fun proc obj:DWORD; fun(MyStruct* obj)
;push ebp
;mov ebp ,esp
;sub esp , 0
local obj2:MyStruct; 在函数内部定义局部变量
; 访问局部结构体变量的字段
lea eax,[obj2];
mov [eax+MyStruct.nNum],0ah
mov byte ptr [eax+MyStruct.cChar],041h
; [ebp+8] => MyStruct* obj
mov eax,[ebp+8];
mov [eax+MyStruct.nNum],0ah
mov byte ptr [eax+MyStruct.cChar],041h
;mov esp , ebp
;pop ebp
ret 4
fun endp
end
在源文件中调用汇编代码.
声明函数原型(注意要使用 extern"C"
extern "C" void __stdcall fun(MyStruct* obj);
直接调用函数即可.
裸函数
在函数名中加上关键字 _declspec(naked)
void _declspec(naked) fun()
{
_asm ret;
}
编译器不会在裸函数的内部生成额外的代码. 写了多少就有多少.
如果裸函数有形参, 那么需要在函数内部自己编写汇编来打开栈帧.
如果裸函数有局部变量
局部变量不能赋初始值
还需自己编写汇编代码开辟局部变量所占的栈空间
// 裸函数
int _declspec(naked) fun(int n)
{
_asm
{
push ebp;
mov ebp,esp;
sub esp,400
}
int m;
m = 200; //对应汇编 mov dword ptr [ebp-8],0C8h
n = 100; //对应汇编 mov dword ptr [ebp+8],64h
_asm {
mov eax, n;
mov esp,ebp;
pop ebp; ret
}
}
x64汇编
函数传参方式: 头4个参数依次使用 rcx,rdx,r8,r9 来传递, 第4个之后的参数使用栈来传递(从右往左) , 栈平衡着是函 数内部.
反汇编引擎和汇编引擎
反汇编引擎 - 能够将一些机器码翻译成汇编代码. 汇编引擎 - 能够将汇编代码翻译器成机器码.
关于 RadAsm 的坑
RadAsm 中十六进制不能以 0x 开头,需要在末尾添加 h
正确示范:mov eax, [100h]
但是执行完在OD里面是 mov eax,100h
想要实现下面的语义,
mov eax, dword ptr [0x100]
需要
mov eax, DS:[100h]
lea eax, [100h] 想实现上面的功能需要写这个代码 mov eax, 100h lea eax, [eax]
栈是什么?栈帧是什么?
线程最少由一个线程内核对象和一个栈组成?
栈: 是 ss 起始的一块特殊的内存空间
(栈)帧: 栈帧是栈中的一块区域,栈帧 以函数为单位
栈的操作
通常栈在调试器的增长方向是自上而下
调试器中,栈视图的最上方指向的是 esp
对栈的操作
入栈 push(a\f) esp-n
出栈 pop(a\f) esp+4
call 和 ret
call = push 下一条指令 + jmp 目标地址
ret = mov eip, [esp], add esp, 4 + n*(4)
汇编3栈帧,参数传递,串操作,混合汇编,x64,asm文件的更多相关文章
- 汇编实现: C库常见函数,串操作指令作用
目录 汇编实现: C库常见函数 一丶汇编实现Strncpy拷贝函数 二丶loads实现Strlen操作. 三丶stos的作用 汇编实现: C库常见函数 一丶汇编实现Strncpy拷贝函数 void _ ...
- X86-64寄存器和栈帧--牛掰降解汇编函数寄存器相关操作
X86-64寄存器和栈帧 概要 说到x86-64,总不免要说说AMD的牛逼,x86-64是x86系列中集大成者,继承了向后兼容的优良传统,最早由AMD公司提出,代号AMD64:正是由于能向后兼容,AM ...
- X86-64寄存器和栈帧
简介 通用寄存器可用于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果.除此之外,它们还各自具有一些特殊功能.通用寄存器的长度取决于机器字长,汇编语言程序员必须熟悉每个寄存器的一般用途和特殊用途, ...
- C语言的函数调用过程(栈帧的创建与销毁)
从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: int Add(int x,int y) { ; sum = x + y; return sum; } int main () { ; ...
- CSAPP阅读笔记-栈帧-来自第三章3.7的笔记-P164-P176
1.基本结构: 如上图所示,是通用的栈帧结构.大致分两块,调用者函数P和被调用者函数Q. 对P来说,要做的工作是把传递参数中多于6个的部分压栈,随后把Q返回时要执行的下一条指令的地址压栈. 对Q来说, ...
- x86汇编之栈与子程序调用
什么是栈 栈与普通数据结构所说的栈的概念是相似的,遵循后进先出原则.不同的是汇编中所说的栈是一个在内存中连续的保存数据的区域,也即是实际存在的内存区域,进栈和出栈遵循后进先出原则. 在x86架构中,栈 ...
- 栈帧%ebp,%esp详解
首先应该明白,栈是从高地址向低地址延伸的.每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息.寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部( ...
- c函数调用过程原理及函数栈帧分析
转载自地址:http://blog.csdn.net/zsy2020314/article/details/9429707 今天突然想分析一下函数在相互调用过程中栈帧的变化,还是想尽量以比 ...
- 对抗栈帧地址随机化/ASLR的两种思路和一些技巧
栈帧地址随机化是地址空间布局随机化(Address space layout randomization,ASLR)的一种,它实现了栈帧起始地址一定程度上的随机化,令攻击者难以猜测需要攻击位置的地址. ...
随机推荐
- 【USACO】The Cow Prom
[题目链接] 点击打开链接 [算法] tarjan求强连通分量 [代码] #include<bits/stdc++.h> #define MAXN 20005 using namespac ...
- python 高性能web框架 gunicorn+gevent
参考链接: http://rfyiamcool.blog.51cto.com/1030776/1276364/ http://www.cnblogs.com/nanrou/p/7026789.html ...
- fastText(二):微博短文本下fastText的应用(一)
众所周知,微博中的内容以短文本居多,文本内容随意性极强,这给建模增加了很大的难度.针对这一问题,这里分享一下fastText在微博短文本的应用. 任务目标简单介绍一下整个任务的目标:给微博内容打上标签 ...
- 当你触摸并按住触摸目标时候,禁止系统默认菜单-webkit-touch-call
当你触摸并按住触摸目标时候,禁止或显示系统默认菜单. -webkit-touch-callout 是一个 不规范的属性(unsupported WebKit property),它没有出现在 CSS ...
- Oracle Escape
select * from tablewhere col like '%\_'escape'\'
- Windows 下openssl安装与配置
编译thirift失败 网上方法很多,大部分是针对32位机的,自己的电脑因为是win7,64位,摸索了很久才安装成功. 环境 WIN7, 64位, vs2005 下载ActivePerl 配置过程中需 ...
- js的NaN变量
js中,我们经常在parseInt函数的时候遇到NaN变量,这个变量到底是什么呢? w3c上这样解释: NaN 属性是代表非数字值的特殊值.该属性用于指示某个值不是数字.可以把 Number 对象设置 ...
- 大数据系列文章-Hadoop的HDFS读写流程(二)
在介绍HDFS读写流程时,先介绍下Block副本放置策略. Block副本放置策略 第一个副本:放置在上传文件的DataNode:如果是集群外提交,则随机挑选一台磁盘不太满,CPU不太忙的节点. 第二 ...
- 关于asp.net网址出现乱码问题的解决方法
背景: asp.net项目,C#,VS2010,.netframework 4.0 创建之初,没有任何问题,随着项目文件的增多,不免很多问题会出现, 最近就莫名其妙的发现我的项目网址多了一段乱码,很是 ...
- 如何访问google?
1:浏览器打开 uzer.me 网站(建议:火狐,不行就谷歌) 2:点击注册,注册一个账号 3:登录账号,进入如下界面,选择火狐浏览器 4:这样就可以进行国外的网站访问了