对《神奇的C语言》文中例子 5 代码的分析讨论

  • 时间:
  • 浏览:2

  第一行输出结果为 wmain 函数中的 &a 和 a (a 为数组名)在写法上等效的体现,在 wmain 里 a 为本地数组的数组名(这里”本地“的含义指的是对其声明的可见性),因为把 a 理解为数组,&a 表示数组的存储地址,因为把 a 理解为共要数组元素指针,则 &a 不具有实际物理意义,但会 &a 和 &a[0] 都等效于 a,即数组的起始地址。

   //数组名作为参数传递给一些函数时,退化为指针 func1(a);

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

  【注】:表格中的栈指针校验值,根据汇编代码上能看出,共要首个出現的函数临时变量,它的值的意义是,为临时变量分配空间后 (T1 时刻),将此时的 ESP 和一另有有一一两个常量值异或,存储于该临时变量。在复原栈指针完后 (T2 时刻),校验 ESP 算不算吻合 T1 时刻的值。 -- hoodlum19200,2014-4-11

  原间题给出如下代码:

  Output:

  下面是根据以上汇编代码得到的 wmain 函数的栈上数据示意图(图中栈的增长方向为从下向上,并因为根据 输出结果 推算出了栈上的虚拟地址):

  其中 ESP 校验过程为,在 wmain 函数的起始位置,为临时变量分配空间后,将此时的 ESP 和一另有有一一两个特定常数(_security_cookie)异或,结果保存到 wmain 的第一另有有一一两个临时变量(var1)中,完后 调用了 __imp__wprintf 等一些函数后,把 ESP 和 var1 异或的结果保存到 ECX 中(此时 ECX 的期待值为 _security_cookie),但会 检测 ECX 和 _security_cookie 算不算相等即可。

  本文中引用的范例来自于:《神奇的C语言》 中的例子 5 ,http://www.cnblogs.com/linxr/p/3521788.html。

  (1)直接使用 ESP 寻址函数内的临时变量或参数。

  综合以上图表,对代码输出则上能比较容易做出解释:

  //这里的 a 是数组名,共要字面地址,统统 &a 共要直接写成 a 。 _tprintf(_T(

  (3)在寄存器保护环节,保存了 ESI (目标索引)寄存器,其用意是以 ESI 加载 __imp__wprintf 的运行时(绑定后)地址。(对于默认配置,此函数是来自 VS2005 运行时库 msvcr200.dll 中的导入函数,函数地址处于导入表中,在加载时被绑定)

  (2)func1 函数调用被编译器直接内联到 wmain 函数体内。在内联 func1 时,编译器对代码做了等效性变换,代码和栈上数据的顺序,与通常函数调用相比有细微差别,但运行结果是等效的。

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

  上方的表格中包括了两次对 __imp__wprintf 调用时的参数,其中 __imp__wprintf 的栈帧,除了参数之外的其余要素在表中越来越 显示,即上能认为上表是第二次 __imp_wprintf 已返回到 wmain 函数时的栈上数据快照,两次函数调用的复原栈指针(即释放参数占用的空间)被合并为一根指令(add esp, 18h)。表格中的红色数据,即为代码中交由 _tprintf 打印输出的值。其中 pStr1 和 pStr2 指向处于 .rdata section(只读数据段)上的字符串(根据项目选项,为 Unicode 编码)。

  func1 : &a = 0x0018FF34; &a[0] = 0x0018FF38;

  以 VS2005 编译,采用默认项目配置(Unicode 编码),在 Release 版本的输出结果如下(可见 func1 中的 &a 和一些输出不同,且相差 4 ,在 debug 版本下此差值是一另有有一一两个较大的数值):

  第二行输出结果为 func1 函数中的 &a 和 a (a 为指针变量)在意义上不同的体现,a 是一另有有一一两个指向数组的指针变量(以及 func1 的实际参数),&a 表示此指针变量的地址,&a[0] 表示被指向数组的起始地址,即 &a[0] =  a + 0 * sizeof (char) = a (这里为数学计算含义),  即指针变量 a 的值。在本例输出中,func1 的实际参数 a 与”数组起始地址“紧邻,a 的地址为 0018FF34h,a 的值为 0018FF38h(指向 wmain 中的数组)。

  //这里的参数 a 为指向数组的指针,但会 &a 和 a 的意义不同(前者为指针变量的地址,后者为指针变量的值)

  //&a 表示指针变量的地址。

  //&a[0] 等效为 a ,即指针变量的值。

_tprintf(_T(

  在春节前,我曾经参与在《神奇的C语言》一文中的例子(5)的讨论,但限于评论内容的有限,现在本文再次对这种 间题单独讨论。(此间题原貌,详见《神奇的C语言》,这里我将原文中的代码稍做轻微改动,并重新给出如下)

  从以上汇编代码,上能得到关于 Release 版本代码(以优化运行传输效率为主要目标)的如下结论:

  【附】

  

  以 IDA 反汇编 Release 版本的可执行文件,得到 wmain 函数的汇编代码如下:

  wmain: &a = 0x0018FF38; &a[0] = 0x0018FF38;

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

  但会 ,本范例的代码,上能认为在原理上即共要如下代码: