比如你用local在栈上定义了一个局部变量LocalVar,你知道实际的指令是什么么?一般都差不多像下面的样子:   
  push   ebp   
  mov   esp,   ebp   
  sub   esp,   4   
  现在栈上就有了4各字节的空间,这就是你的局部变量。   
  接下来,你执行mov   LocalVar,   4,那么实际的指令又是什么?是这样:   
  mov   dword   ptr   [ebp-4],   4   
  于是,这个局部变量的“地址”就是ebp-4——显然,它不是一个固定的地址。现在需要将它的“地址”作为参数传给某个函数,你这样写:   
  invoke/call   SomeFunc,   addr   LocalVar   
  实际生成的指令是:   
  lea   eax,   [ebp-4]   
  push   eax   
  call   SomeFunc   
  当然,你也可以写成:   
  mov   eax,   ebp   
  sub   eax,   4   
  push   eax   
  call   SomeFunc   
  看到了,这里多了一条指令。这就是lea的好处。于是,lea又多了一个非常美妙的用途:作简单的算术计算,特别是有了32位指令的增强寻址方式,更是“如虎添翼”:   
  比如你要算EAX*4+EBX+3,结果放入EDX,怎么办?   
  mov   edx,   eax   
  shl   edx,   2   
  add   edx,   ebx   
  add   edx,   3   
  现在用lea一条指令搞定:   
  lea   edx,   [ebx+eax*4+3] // 相当于 lea edx, [eax*4+ebx+3],这里完全与内存地址无关

lea的英文解释是: Load Effective Address.(加入有效地址,开始迷惑效地址是什么???既然是有效地址与mov ax , [address] 又有什么不同呢?其实他们都是等效的。 后来知道实际上是一个偏移量可以是立即数,也可以是经过四则运算的结果,更省空间,更有效率)

参考:
http://blog.csdn.net/lostspeed/article/details/8959142
http://www.cnitblog.com/textbox/articles/51912.html

-------------------------------------------------------------------------------

亲自花了两小时研究一个小例子,VC6 Debug版本:

源程序:

  1. #include "stdafx.h"
  2.  
  3. int add(int i)
  4. {
  5. int x = i+;
  6. return x;
  7. }
  8.  
  9. int fun()
  10. {
  11. int i=;
  12. return add(i);
  13. }
  14.  
  15. int main()
  16. {
  17. int y = fun();
  18. return y;
  19. }

汇编程序:

  1. // 知识点:
  2. // ESP:寄存器存放当前线程的栈顶指针(堆栈指针),压入堆栈的数据越多,ESP也就越来越小,用它只可访问栈顶
  3. // EBP:寄存器存放当前线程的栈底指针(基址指针,base pointer),用它可直接存取堆栈中的数据
  4. // __cdecl 所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈
  5.  
  6. // 编译器提前生成的函数跳转代码,一共3个函数,地址准确对应:
  7. @ILT+(?add@@YAHH@Z):
  8. jmp add () // 执行前 ESP = 0012FED0
  9. // 执行后 ESP = 0012FED0 无条件跳转,不需要压栈
  10. @ILT+(?fun@@YAHXZ):
  11. 0040100A jmp fun ()
  12. @ILT+(_main):
  13. 0040100F jmp main (004010c0)
  14.  
  15. int add(int i)
  16. {
  17. push ebp // 执行前 ESP = 0012FED0 EBP = 0012FF28
  18. // 执行后 ESP = 0012FECC EBP = 0012FF28 (ESP减少4,第二次偏移)
  19. mov ebp,esp // 执行后 ESP = 0012FECC EBP = 0012FECC (保护esp的值,这样esp可随便使用了)
  20. sub esp,44h // 执行后 ESP = 0012FE88 EBP = 0012FECC 给栈增加44h的空间
  21. push ebx // 执行后 ESP = 0012FE84
  22. push esi // 执行后 ESP = 0012FE80
  23. push edi // 执行后 ESP = 0012FE7C
  24. lea edi,[ebp-44h] // 执行后 EDI = 0012FE88
  25. 0040103C mov ecx,11h
  26. mov eax,0CCCCCCCCh
  27. rep stos dword ptr [edi]
  28.  
  29. int x = i+;
  30. mov eax,dword ptr [ebp+] // 执行前 EBP = 0012FECC,执行 EBP + = 0012FED4,就取到了fun函数压栈的数据
  31. 0040104B add eax, // 前面call的时候导致了两次减4,所以要加8。此时用EBP访问数据。此时eax也是空闲的
  32. 0040104E mov dword ptr [ebp-],eax // 把eax的值放到[ebp-]里,相当于为x赋值了
  33. return x;
  34. mov eax,dword ptr [ebp-] // 函数的返回值为eax
  35. }
  36. pop edi
  37. pop esi
  38. pop ebx
  39. mov esp,ebp // 恢复ESP,即退出函数前,栈顶指针不变
  40. pop ebp // 恢复EBP,站底指针
  41. 0040105A ret
  42. // 这里不用加44,因为add没有调用其它函数
  43. int fun()
  44. {
  45. push ebp
  46. mov ebp,esp
  47. sub esp,44h
  48. push ebx
  49. push esi
  50. push edi
  51. lea edi,[ebp-44h]
  52. 0040107C mov ecx,11h
  53. mov eax,0CCCCCCCCh
  54. rep stos dword ptr [edi]
  55.  
  56. int i=;
  57. mov dword ptr [ebp-],0Ah // i是函数内第一个局部变量,占据了[ebp-]的位置,因此把0Ah这个值放里
  58. : return add(i);
  59. 0040108F mov eax,dword ptr [ebp-] // 把这个i局部变量的值,临时放到eax里,目的是为了方便压栈
  60. push eax // 准备调用add函数,要给它准备所有参数,即把i的值压栈(cdeclf方式)。
  61. // 执行前 ESP = 0012FED8 EBP = 0012FF28
  62. // 执行后 ESP = 0012FED4 EBP = 0012FF28 (ESP减少4)
  63. // 注意,ESP的值在整个CPU里是唯一的,各个函数之间会相互影响(特别是cdecl调用方式下),push会导致ESP的值减4(可观察CPU状态)
  64. call @ILT+(add) () // 执行前 ESP = 0012FED4 EBP = 0012FF28
  65. // 执行后 ESP = 0012FED0 EBP = 0012FF28 (ESP减少4,第一次偏移。call语句自动就包括了压栈功能)
  66. // 一共两次减小4
  67. add esp,
  68. }
  69. 0040109B pop edi
  70. 0040109C pop esi
  71. 0040109D pop ebx
  72. 0040109E add esp,44h // 调用者清除,对应add函数造成的esp减少了44
  73. 004010A1 cmp ebp,esp
  74. 004010A3 call __chkesp ()
  75. 004010A8 mov esp,ebp
  76. 004010AA pop ebp
  77. 004010AB ret
  78.  
  79. int main()
  80. {
  81. int y = fun();
  82. 004010D8 call @ILT+(fun) (0040100a) // 呼叫语句,执行fun函数。此函数执行完毕后,会把运算结果放在eax里。
  83. 004010DD mov dword ptr [ebp-],eax // 本地变量y占据了[ebp-4]的位置,所以需要把前面函数的执行结果eax放到这个位置
  84. return y;
  85. 004010E0 mov eax,dword ptr [ebp-] // 把[ebp-]的值放到eax里,相当于main函数的返回值已经在eax里了
  86. }
  87. 004010E3 pop edi
  88. 004010E4 pop esi
  89. 004010E5 pop ebx
  90. 004010E6 add esp,44h // 调用者清除,对应fun函数造成的esp减少了44
  91. 004010E9 cmp ebp,esp
  92. 004010EB call __chkesp ()
  93. 004010F0 mov esp,ebp
  94. 004010F2 pop ebp
  95. 004010F3 ret

不知道release版本有没有这个ESP-44的问题,之所以花了2小时,就是因为这个原因。可是VC++为什么要这样做呢?难道是为了给调用函数的时候,留出足够的栈空间(够放10个参数了,另一个4字节空间是被调用函数自己造成的)?

----------------------------------------------------------------------------------

再接再厉,使用W32Dasm反汇编并精简之后得到的代码(VC自身也可看Release版的汇编源码):

  1. Disassembly of File: C:\Dev\ChinaZip_Crack\Tools\w32dasm\lll_cdcel.exe
  2. Code Offset = , Code Size =
  3. Data Offset = , Data Size =
  4.  
  5. Number of Objects = (dec), Imagebase = 00400000h
  6.  
  7. Object01: .text RVA: Offset: Size: Flags:
  8. Object02: .rdata RVA: Offset: Size: Flags:
  9. Object03: .data RVA: Offset: Size: Flags: C0000040
  10.  
  11. +++++++++++++++++++ IMPORT MODULE DETAILS +++++++++++++++
  12.  
  13. Import Module : KERNEL32.dll // 导入系统API地址,地址都很小,低于0x004000000
  14. Addr:000054F8 hint(00CA) Name: GetCommandLineA
  15. Addr:0000550A hint() Name: GetVersion
  16. Addr: hint(007D) Name: ExitProcess
  17. Addr: hint(029E) Name: TerminateProcess
  18. Addr:0000553A hint(00F7) Name: GetCurrentProcess
  19. Addr:000056D6 hint(00BF) Name: GetCPInfo
  20. Addr:000056E2 hint(00B9) Name: GetACP
  21. Addr:000056EC hint() Name: GetOEMCP
  22. Addr:000056F8 hint(02BB) Name: VirtualAlloc
  23. Addr: hint(01A2) Name: HeapReAlloc
  24. Addr: hint(013E) Name: GetProcAddress
  25. Addr: hint(01C2) Name: LoadLibraryA
  26. Addr: hint(01E4) Name: MultiByteToWideChar
  27. Addr:0000574E hint(01BF) Name: LCMapStringA
  28. Addr:0000575E hint(01C0) Name: LCMapStringW
  29. Addr:0000576E hint() Name: GetStringTypeA
  30. Addr: hint() Name: GetStringTypeW
  31.  
  32. +++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++
  33. //********************** Start of Code in Object .text **************
  34. Program Entry Point = (C:\Dev\ChinaZip_Crack\Tools\w32dasm\lll_cdcel.exe File Offset:)
  35.  
  36. // 估计是 int add(int i)
  37.  
  38. : 8B442404 mov eax, dword ptr [esp+]
  39. : 83C004 add eax,
  40. : C3 ret
  41.  
  42. * Referenced by a (U)nconditional or (C)onditional Jump at Address:
  43. |:(U)
  44.  
  45. // 估计是 int fun()
  46.  
  47. : 6A0A push 0000000A
  48. : E8E9FFFFFF call
  49. : 83C404 add esp,
  50. :0040101A C3 ret
  51.  
  52. * Referenced by a CALL at Address:
  53. |:004010DF
  54.  
  55. : E9EBFFFFFF jmp 00401010 // 一句单独的跳转语句(准备跳转到fun函数),main函数就是靠它调用fun函数
  56.  
  57. //******************** Program Entry Point ********
  58. // 估计是 int main(),那么多前期准备!!!
  59.  
  60. : push ebp
  61. : 8BEC mov ebp, esp
  62. : 6AFF push FFFFFFFF
  63. : 68A0504000 push 004050A0
  64. :0040103A 688C1C4000 push 00401C8C
  65. :0040103F 64A100000000 mov eax, dword ptr fs:[]
  66. : push eax
  67. : mov dword ptr fs:[], esp
  68. :0040104D 83EC10 sub esp,
  69. : push ebx
  70. : push esi
  71. : push edi
  72. : 8965E8 mov dword ptr [ebp-], esp
  73.  
  74. * Reference To: KERNEL32.GetVersion, Ord:0174h
  75. |
  76. : FF1504504000 Call dword ptr []
  77. :0040105C 33D2 xor edx, edx
  78. :0040105E 8AD4 mov dl, ah
  79. : 8915E4844000 mov dword ptr [004084E4], edx
  80. : 8BC8 mov ecx, eax
  81. : 81E1FF000000 and ecx, 000000FF
  82. :0040106E 890DE0844000 mov dword ptr [004084E0], ecx
  83. : C1E108 shl ecx,
  84. : 03CA add ecx, edx
  85. : 890DDC844000 mov dword ptr [004084DC], ecx
  86. :0040107F C1E810 shr eax,
  87. : A3D8844000 mov dword ptr [004084D8], eax
  88. : 6A00 push
  89. : E8A80A0000 call 00401B36
  90. :0040108E pop ecx
  91. :0040108F 85C0 test eax, eax
  92. : jne 0040109B
  93. : 6A1C push 0000001C
  94. : E89A000000 call
  95. :0040109A pop ecx
  96.  
  97. * Referenced by a (U)nconditional or (C)onditional Jump at Address:
  98. |:(C)
  99. |
  100. :0040109B 8365FC00 and dword ptr [ebp-],
  101. :0040109F E872070000 call
  102.  
  103. * Reference To: KERNEL32.GetCommandLineA, Ord:00CAh
  104. |
  105. :004010A4 FF1500504000 Call dword ptr []
  106. :004010AA A3D8894000 mov dword ptr [004089D8], eax
  107. :004010AF E830060000 call 004016E4
  108. :004010B4 A3C0844000 mov dword ptr [004084C0], eax
  109. :004010B9 E8D9030000 call
  110. :004010BE E81B030000 call 004013DE
  111. :004010C3 E890000000 call
  112. :004010C8 A1F4844000 mov eax, dword ptr [004084F4]
  113. :004010CD A3F8844000 mov dword ptr [004084F8], eax
  114. :004010D2 push eax
  115. :004010D3 FF35EC844000 push dword ptr [004084EC]
  116. :004010D9 FF35E8844000 push dword ptr [004084E8]
  117. :004010DF E83CFFFFFF call // 估计是调用fun()函数
  118. :004010E4 83C40C add esp, 0000000C
  119. :004010E7 8945E4 mov dword ptr [ebp-1C], eax
  120. :004010EA push eax
  121. :004010EB E895000000 call
  122. :004010F0 8B45EC mov eax, dword ptr [ebp-]
  123. :004010F3 8B08 mov ecx, dword ptr [eax]
  124. :004010F5 8B09 mov ecx, dword ptr [ecx]
  125. :004010F7 894DE0 mov dword ptr [ebp-], ecx
  126. :004010FA push eax
  127. :004010FB push ecx
  128. :004010FC E859010000 call 0040125A
  129. : pop ecx
  130. : pop ecx
  131. : C3 ret

反汇编知识匮乏,目前只能到这步了,先混个脸熟。。。

Lea指令计算地址(用于四则混合运算),附上一个函数调用例子及其反汇编代码,很清楚的更多相关文章

  1. 如何处理加括号的四则混合运算表达式——基于二叉树的实现(Eclipse平台 Java版)

    记得上<数据结构>课程时,利用栈的特性解决过四则混合运算表达式.而如今在编写小型关系数据库的时候,编译部分要处理where后面的逻辑表达式——检查语法正确与否的同时,还要将信息传给下一个接 ...

  2. MathExamV2.0四则混合运算计算题生成器

    MathExamV2.0四则混合运算计算题生成器----211606360 丁培晖 211606343 杨宇潇 一.预估与实际 PSP2.1 Personal Software Process Sta ...

  3. C语言编程学习:写的秒速计算四则混合运算项目

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  4. 用C++实现的有理数(分数)四则混合运算计算器

    实现目标 用C++实现下图所示的一个console程序: 其中: 1.加减乘除四种运算符号分别用+.-.*./表示, + 和 - 还分别用于表示正号和负号. 2.分数的分子和分母以符号 / 分隔. 3 ...

  5. 软件工程学习之小学四则混合运算出题软件 Version 1.00 设计思路及感想

    对于小学四则混合运算出题软件的设计,通过分析设计要求,我觉得为了这个软件在今后便于功能上的扩充,可以利用上学期所学习的<编译原理>一课中的LL1语法分析及制导翻译的算法来实现.这样做的好处 ...

  6. java 解析四则混合运算表达式并计算结果

    package ch8; import java.util.LinkedList; import java.util.List; import java.util.Stack; /** * 四则混合运 ...

  7. 软件工程学习之小学四则混合运算出题软件 Version 1.1 设计思路及感想

    继上次采用形式文法来生成混合运算的算式,由于算法中没有引入控制参数而导致容易产生形式累赘(多余的括号等)的算式.本次更新决定采用一种更为简单有效的生成方式,由给出的一个随机的最终答案S,通过给定的一个 ...

  8. 带括号的四则混合运算的算符优先算法-----java实现

    1:主方法 package com.baidu; import java.text.NumberFormat;import java.util.ArrayList;import java.util.S ...

  9. web四则混合运算3

    一.程序要求: 可以控制下列参数: 是否有乘除法: 是否有括号(最多可以支持十个数参与计算): 数值范围: 加减有无负数: 除法有无余数!   二.设计思路 要求能够通过参数来控制有无乘除法,加减有无 ...

随机推荐

  1. 【转】Android 应用测试总结

    前提所有的功能分支已完成 启动:1. 启动入口:桌面正常启动,最近运行启动,所有程序列表中启动,锁屏快捷启动2. 其他入口:从其他程序开启应用,从外部以文件形式打开应用(如果有)3. 退回:从其他程序 ...

  2. NSObject中的isa到底是个什么?

    首先看一下NSObject的定义: @interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; } 官方解释:Eve ...

  3. Velocity 入门(一)

    Velocity是一种Java模版引擎技术,该项目由Apache提出.因为非常好用,和工作中有啥用,所以我在在理简单的入门一下. 网上找了很多教程,写的不是很明白,要么就是全部拷贝下来时候运行不起来. ...

  4. SQL SERVER格式化字符串位数,不足补零

    本文举例在SQLSERVER中将1格式化为001的方法: 1.方法一SQL语句执行如下: ,) as col 2.方法二SQL语句执行如下: ,) ,) as col 下面是C#代码实现方法: ; & ...

  5. javascript创建对象和属性的几种方式

    一句话,javascript里面的对象,即是函数.方法. (一)第一种: a.声明对象:var JHSoft = JHSoft || {}; 或者 var JHSoft=new Object(); b ...

  6. Mac OS X 配置环境变量

    /etc/profile:/etc/bashrc 是针对系统所有用户的全局变量,只有root用户才能修改这两个文件,对一般用户来说是他们是只读的.一般用户要想修改它们,可以在命令前加sudo,意思是以 ...

  7. 10.29_Extjs-lovcombo

    (1) Ext.ux.form.LovCombo多选下拉框 :http://www.iteye.com/topic/340900 (2)combox:icon,lovcombo:icon (3) (4 ...

  8. IOS 学习笔记 2015-03-24 OC-API-网络访问-案例一

    // // WPSuggest.h // OC-API-网络访问 // // Created by wangtouwang on 15/3/24. // Copyright (c) 2015年 wan ...

  9. (转)IOS内存管理 retain release

    obj-c本质就是"改进过的c语言",大家都知道c语言是没有垃圾回收(GC)机制的(注:虽然obj-c2.0后来增加了GC功能,但是在iphone上不能用,因此对于iOS平台的程序 ...

  10. windows phone 扫描二维码

    在网上找了找扫描二维码的例子,利用ZXing库实现(下载),提供的Silverlight版本的下载,在网上搜了一下已经有wp的dll可用了,不过网上实现的条码扫描的例子还都是用的Silverlight ...