一、请问这句汇编代码什么意思: LEA EAX,DWORD PTR SS:[ESP+4C],
将SS(stack segment堆栈段)中ESP+4C这个“地址”(注意是地址,而非地址中的值)以双字(即4个字节)放入到EAX这个寄存器中(此寄存器可存储4个字节,正好匹配你放进来的4个字节长度的地址)。“DWORD PTR ”就是将内存单元指定为双字(同理,WORD-PTR就是指定为字,BYTE-PTR就是指定为字节)。
另外mov与lea的区别:mov ax,BUFF ;是把BUFF这个内存单元中的数据放入到ax寄存器中
而 lea ax,BUFF ;是把BUFF这个内存单元的地址放入到ax寄存器中
两者区别就是一个传递的是内容,一个传递的是地址。
二、易语言置代码 ({ }) pushad () 下如何写以下代码
是 置入代码() 吧? 那个是指机器码,不是汇编指令,比如说汇编指令中的NOP对应的机器码是90(16进制),转换为10进制就是144, 这里的用法就是:置入代码({144}) ~
调用格式: 〈无返回值〉 置入代码 (通用型 代码数据) - 系统核心支持库->;其他
英文名称:MachineCode
在编译后文件代码段中当前语句位置置入指定的机器指令码数据。本命令为高级命令。
参数的名称为“代码数据”,类型为“通用型(all)”。欲置入到代码段中的机器指令码数据,可以是字节集数据或二进制文件名文本。
操作系统需求: Windows、Linux
三、vc 语言中函数返回值如何定义
教科书中一般说,在C/C++中,函数通过eax寄存器返回结果。
如果结果不大于4字节,则eax就是它的值;如果大于4字节,则返回存放它的内存地址。请思考如下的问题:如果函数返回的结果大于4字节,那么它被存放到哪里了?一般情况下,局部变量通过add esp -4*n或者push ecx从堆栈获得存储空间。
如果结果也像局部变量这般,那么返回以后,它有可能被后续操作覆盖掉。所以应该把在调用函数前就为它分配空间。
这段空间分配在哪里?函数如何使用它?这是进一步引申出来的问题。下面我们就做几个实验来观察一下。
(如果想直接看结果,可以跳到本文最后的总结。)先介绍实验环境。
C源程序经过Microsoft Visual C++ 6.0自带的命令行工具cl.exe(版本12.0.8168.0)编译,不加任何参数。先看返回值为4字节的情况。
源代码如下:typedef struct stSize4{ char a; char b; short c;} stSize4;stSize4 stFunc(short num){ stSize4 temp = {'t','h',num}; return temp;}void main(){ stSize4 target; target = stFunc(1745);}编译后,反汇编结果如下:00401000 push ebp ;stFunc()00401001 mov ebp, esp00401003 push ecx ;4字节局部变量temp00401004 mov byte ptr ss:[ebp-4], 7400401008 mov byte ptr ss:[ebp-3], 680040100C mov ax, word ptr ss:[ebp+8] ;取出参数00401010 mov word ptr ss:[ebp-2], ax00401014 mov eax, dword ptr ss:[ebp-4] ;把值直接赋给eax00401017 mov esp, ebp00401019 pop ebp0040101A ret0040101B push ebp ;main()0040101C mov ebp, esp0040101E push ecx ;4字节局部变量target0040101F push 6D1 ;参数入栈00401024 call 0040100000401029 add esp, 4 ;堆栈平衡0040102C mov dword ptr ss:[ebp-4], eax ;把eax的值保存0040102F mov esp, ebp00401031 pop ebp00401032 ret正如教科书所言,结果被直接存储在eax中返回了。接下来我们写一个返回值为8字节的程序。
typedef struct stSize8{ short a; short b; short c; short d;} stSize8;stSize8 stFunc(short num){ stSize8 temp = {100,200,300,num}; return temp;}void main(){ stSize8 target; target = stFunc(1745);}反汇编后,出现了一些不同的情况:00401000 push ebp ;stFunc()00401001 mov ebp, esp00401003 sub esp, 8 ;8字节局部变量temp00401006 mov word ptr ss:[ebp-8], 640040100C mov word ptr ss:[ebp-6], 0C800401012 mov word ptr ss:[ebp-4], 12C00401018 mov ax, word ptr ss:[ebp+8] ;取出参数0040101C mov word ptr ss:[ebp-2], ax00401020 mov eax, dword ptr ss:[ebp-8] ;低位的值存到eax00401023 mov edx, dword ptr ss:[ebp-4] ;高位的值存到edx00401026 mov esp, ebp00401028 pop ebp00401029 ret0040102A push ebp ;main()0040102B mov ebp, esp0040102D sub esp, 8 ;8字节局部变量target00401030 push 6D1 ;参数入栈00401035 call 004010000040103A add esp, 4 ;堆栈平衡0040103D mov dword ptr ss:[ebp-8], eax ;把eax的值存到局部变量00401040 mov dword ptr ss:[ebp-4], edx ;把edx的值存到局部变量00401043 mov esp, ebp00401045 pop ebp00401046 ret此时并不是通常说的eax返回一个地址,而是使用eax存储8位结果的低4位值,同时使用ebx存储8位结果的高四位值,一并返回给调用者。再来写一个3字节的:typedef struct stSize3{ char a; char b; char c;} stSi www.hbbz08.com ze3;stSize3 stFunc(char ch){ stSize3 temp = {'n','k',ch}; return temp;}void main(){ stSize3 target; target = stFunc('c');}反汇编出来的结果又和上述两种情况有很大不同:00401000 push ebp ;stFunc()00401001 mov ebp, esp00401003 push ecx ;4字节局部变量temp00401004 mov byte ptr ss:[ebp-4], 6E00401008 mov byte ptr ss:[ebp-3], 6B0040100C mov al, byte ptr ss:[ebp+C] ;取出参数,注意是+C而不是+80040100F mov byte ptr ss:[ebp-2], al00401012 mov ecx, dwor www.hnnedu.com d ptr ss:[ebp+8] ;此时ebp+8是调用者临时空间地址00401015 mov dx, word ptr ss:[ebp-4]00401019 mov word ptr ds:[ecx], dx ;把temp的值拷到临时空间0040101C mov al, byte ptr ss:[ebp-2]0040101F mov byte ptr ds:[ecx+2], al ;分两次拷,因为是3字节内容00401022 mov eax, dword ptr ss:[ebp+8] ;最后把地址给eax返回00401025 mov esp, ebp00401027 pop ebp00401028 ret00401029 push ebp ;main()0040102A mov ebp, esp0040102C sub esp, 8 ;4字节局部变量target,4字节临时空间0040102F push 63 ;参数入栈00401031 lea eax, dword ptr ss:[ebp-8] ;/临时空间地址入栈00401034 push eax ;\注意和参数的先后顺序!00401035 call 004010000040103A add esp, 8 ;堆栈平衡,注意是8字节0040103D mov cx, word ptr ds:[eax] ;取出返回的地址处的值00401040 mov word ptr ss:[ebp-4], cx ;存到局部变量target中00401044 mov dl, byte ptr ds:[eax+2]00401047 mov byte ptr ss:[ebp-2], dl0040104A mov esp, ebp0040104C pop ebp0040104D ret首先,在main()中分配了8字节的堆栈空间,而不是temp所占的3字节。
这8字节的由来是:考虑。
四、MOV [0],ECX和MOV DWORD PTR FS:[0],ECX有什么区别
MOV [0],ECX ;#赋给默认段DS(x86) 即ds:0
MOV DWORD PTR FS:[0],ECX ;#指明是段FS 即FS:0
-------------------------
DWORD:双字的意思
----------
FS:
DS:
SS:
这几个是首地址吧
----------
DWORD PTR FS:
DWORD PTR DS:
DWORD PTR SS:
意思:
将所给地址处的数据作双字处理
即取4个字节
五、请问mov EBP,ESP ESP怎么找呀
sub esp,1 在堆栈中留出局部变量的空间看看下面的文章对你的汇编语言一定有帮助!汇编中参数的传递和堆栈修正 在 Win32汇编中,我们经常要和 Api 打交道,另外也会常常使用自己编制的类似于 Api 的带参数的子程序,本文要讲述的是在子程序调用的过程中进行参数传递的概念和分析。
一般在程序中,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序(或者被调用者)的参数压入堆栈,子程序在堆栈取出相应的值再使用,比如说,如果你要调用 SubRouting(Var1,Var2,Var3),编译后的最终代码可能是push Var3push Var2push Var1call SubRoutingadd esp,12也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果,这就是我在前面使用“可能”这两个字的原因:各种语言中调用子程序的约定是不同的,它们的不同点见下表:C SysCall StdCall Basic Fortran Pascal参数从左到右 是 是 是参数从右到左 是 是 是调用者清除堆栈 是允许使用:VARARG 是 是 是VARARG 表示参数的个数可以是不确定的,有一个例子就是 C 中的 printf 语句,在上表中,StdCall 的定义有个要说明的地方,就是如果 StdCall 使用 :VARARG 时,是由调用者清除堆栈的,而在没有:VARARG时是由被调用者清除堆栈的。
在 Win32 汇编中,约定使用 StdCall 方式,所以我们要在程序开始的时候使用 .model stdcall 语句。也就是说,在 API 或子程序中,最右边的参数先入堆栈,然后子程序在返回的时候负责校正堆栈,举例说明,如果我们要调用 MessageBox 这个 API,因为它的定义是 MessageBox(hWnd,lpText,lpCaption,UType) 所以在程序中要这样使用:push MB_OKpush offset szCaptionpush offset szTextpush hWndcall MessageBox。
我们不必在 API 返回的时候加上一句 add sp,4*4 来修正堆栈,因为这已经由 MessageBox 这个子程序做了。在 Windows API 中,唯一一个特殊的 API 是 wsprintf,这个 API 是 C 约定的,它的定义是 wsprintf(lpOut,lpFormat,Var1,Var2。
),所以在使用时就要:push 1111push 2222push 3333push offset szFormatpush offset szOutcall wsprintfadd esp,4*5下面要讲的是子程序如何存取参数,因为缺省对堆栈操作的寄存器有 ESP 和 EBP,而 ESP是堆栈指针,无法暂借使用,所以一般使用 EBP 来存取堆栈,假定在一个调用中有两个参数,而且在 push 第一个参数前的堆栈指针 ESP 为 X,那么压入两个参数后的 ESP 为 X-8,程序开始执行 call 指令,call 指令把返回地址压入堆栈,这时候 ESP 为 X-C,这时已经在子程序中了,我们可以开始使用 EBP 来存取参数了,但为了在返回时恢复 EBP 的值,我们还是再需要一句 push ebp 来先保存 EBP 的值,这时 ESP 为 X-10,再执行一句 mov ebp,esp,根据右图可以看出,实际上这时候 [ebp + 8] 就是参数1,[ebp + c]就是参数2。另外,局部变量也是定义在堆栈中的,它们的位置一般放在 push ebp 保存的 EBP 数值的后面,局部变量1、2对应的地址分别是 [ebp-4]、[ebp-8],下面是一个典型的子程序,可以完成第一个参数减去第二个参数,它的定义是:MyProc proto Var1,Var2 ;有两个参数local lVar1,lVar2 ;有两个局部变量注意,这里的两个 local 变量实际上没有被用到,只是为了演示用,具体实现的代码是:MyProc procpush ebpmov ebp,espsub esp,8mov eax,dword ptr [ebp + 8]sub eax,dword ptr [ebp + c]add esp,8pop ebpret 8MyProc endp现在对这个子程序分析一下,push ebp/mov ebp,esp 是例行的保存和设置 EBP 的代码,sub esp,8 在堆栈中留出两个局部变量的空间,mov /add 语句完成相加,add esp,8 修正两个局部变量使用的堆栈,ret 8 修正两个参数使用的堆栈,相当于 ret / add esp,8 两句代码的效果。
可以看出,这是一个标准的 Stdcall 约定的子程序,使用时最后一个参数先入堆栈,返回时由子程序进行堆栈修正。当然,这个子程序为了演示执行过程,使用了手工保存 ebp 并设置局部变量的方法,实际上,386 处理器有两条专用的指令是完成这个功能用的,那就是 Enter 和 Leave,Enter 语句的作用就是 push ebp/mov ebp,esp/sub esp,xxx,这个 xxx 就是 Enter 的,Leave 则完成 add esp,xxx/pop ebp 的功能,所以上面的程序可以改成:MyPorc procenter 8,0mov eax,dword ptr [ebp + 8]sub eax,dword ptr [ebp + c]leaveret 8MyProc endp好了,说到这儿,参数传递的原理也应该将清楚了,还要最后说的是,在使用 Masm32 编 Win32 汇编程序的时候,我们并不需要记住 [ebp + xx] 等麻烦的地址,或自己计算局部变量需要预留的堆栈空间,还有在 ret 时计算要加上的数值,Masm32 的宏指令都已经把这些做好了,如在 Masm32 中,上面的程序只要写成为:MyProc proc Var1,Var2local lVar1,lVar2mov eax,Var1sub eax,Var2retMyProc endp编译器会自动的在 mov eax,Var1 前面。
六、push ebp mov ebp,esp sub esp,4C mov dword ptr ss:[ebp
1,push ebp;保存ebp内容以便调用完后恢复;此时esp <- esp - 4;
2,move ebp esp; 保存调用前栈顶地址以便调用完后恢复 此时,esp ebp 都指向栈顶,esp作为段寄存器访问堆栈段,ebp作为基址寄存器访问堆栈段中的堆栈帧即稍后要分配的 0x4c个字节;
3,sub esp 4c 即2里面所说的给调用函数分配0x4c字节的局部变量空间堆栈帧;
4,mov dword ptr ss:[ebp - 4], 0; 把立即数0x0保存到 ss:[ebp -4]地址里,即 ss 前缀就是说你所要使用的是ss即堆栈段,ebp可以说是基址,ebp-4即是步骤三分配的04c空间的第一个双字地址,也就是把0扩展成32位然后保存到堆栈帧里面,一般作为调用函数的第一个参数;相应后面如果还有类似mov dword ptr ss:[ebp - n*4] n = 1 ,2,3 等 都是作为函数的参数1,2 ,3。。.;
5,push 8;这是立即数入栈, 具体是 esp <- esp - 4; mov dword ptr [ esp ]; 一般也是作为函数的静态参数使用。
函数调用部分就不说了,给你讲下函数调用完后返回的操作
1,mov esp ebp; 这就是步骤2的恢复,使得esp指向步骤二的栈顶;
2, pop ebp; 这就是把开始保存在堆栈中的ebp 恢复到ebp里面;此时esp <- esp + 4;就是恢复步骤一的状态,此次函数调用完毕;
转载请注明出处51数据库 » movwordptrss:[esp