关于编译型语言函数的调用(三)(一)

2015-01-27 10:19:18 · 作者: · 浏览: 56

?

类delete就不说了,有兴趣的朋友自己跟踪看看吧,提示一下:析构函数也有参数和返回值.

?

下面我们看下裸函数,裸函数从执行效率上是可以与汇编语言媲美的,然而它不太好逆过来说,我们就顺着说吧

前面说得构造函数的临时堆栈,恢复寄存器等等,有人给了个名称叫prolog和epilog

而裸函数并没有自动编译这些部分,其实说白了,裸函数相当于汇编语言中的一个标签,其调用受前面几种约定约束

此外空的裸函数是什么都不做的,非必要的不做,必要的也不做,就是什么都要自己实现,其实就是要自己汇编

当然了,裸函数不能是类的成员函数,举例中我们使用__cdecl约定,直接照搬fnDefaultCall的,不管执行效率

?

132:      ret = fnNakedCall(19, 20, 21, &var1);
00401428   lea         edx,[ebp-14h]
0040142B   push        edx
0040142C   push        15h
0040142E   push        14h
00401430   push        13h
00401432   call        @ILT+15(fnNakedCall) (00401014)
00401437   add         esp,10h
0040143A   mov         dword ptr [ebp-18h],eax
133:
可以看出调用约定符合__cdecl的约定,那么跟踪一下看看:

?

?

68:
69:   __declspec(naked) int __cdecl fnNakedCall(int arg1, short arg2, char arg3, void *arg4)
70:   {
004012D0   push        ebp
71:       // 1. 到这里所有寄存器的值与调用前一样
72:       // 2. 用变量名引用任何局部变量等同于引用主调函数变量或参数
73:       // 3. 必须负责寄存器的维护, 这里函数作为__cdecl
74:       __asm{
75:           push        ebp                 ; prolog begin
76:           mov         ebp, esp
004012D1   mov         ebp,esp
77:           sub         esp, 50h
004012D3   sub         esp,50h
78:           push        ebx
004012D6   push        ebx
79:           push        esi
004012D7   push        esi
80:           push        edi
004012D8   push        edi
81:           lea         edi, [ebp-50h]
004012D9   lea         edi,[ebp-50h]
82:           mov         ecx, 14h
004012DC   mov         ecx,14h
83:           mov         eax, 0CCCCCCCCh
004012E1   mov         eax,0CCCCCCCCh
84:           rep stos    dword ptr [edi]     ; prolog end
004012E6   rep stos    dword ptr [edi]
85:
86:           // var1 = arg1;
87:           mov         eax, dword ptr [ebp + 8]        ; [esp + 8]
004012E8   mov         eax,dword ptr [ebp+8]
88:           mov         dword ptr [ebp-4], eax          ; [esp - 4]
004012EB   mov         dword ptr [ebp-4],eax
89:           // var2 = arg2;
90:           mov         cx, word ptr [ebp + 0Ch]
004012EE   mov         cx,word ptr [ebp+0Ch]
91:           mov         word ptr [ebp - 8], cx
004012F2   mov         word ptr [ebp-8],cx
92:           // var3 = arg3;
93:           mov         dl, byte ptr [ebp + 10h]
004012F6   mov         dl,byte ptr [ebp+10h]
94:           mov         byte ptr [ebp - 0Ch], dl
004012F9   mov         byte ptr [ebp-0Ch],dl
95:           // p = (int *)arg4;
96:           mov         eax, dword ptr [ebp + 14h]
004012FC   mov         eax,dword ptr [ebp+14h]
97:           mov         dword ptr [ebp - 10h], eax
004012FF   mov         dword ptr [ebp-10h],eax
98:           // *p = -1;
99:           mov         ecx, dword ptr [ebp - 10h]
00401302   mov         ecx,dword ptr [ebp-10h]
100:          mov         dword ptr [ecx], 0FFFFFFFFh
00401305   mov         dword ptr [ecx],0FFFFFFFFh
101:          // return 22;
102:          mov         eax, 16h            ; 0x16 = 22
0040130B   mov         eax,16h
103:
104:          pop         edi                 ; epilog begin
00401310   pop         edi
105:          pop         esi
00401311   pop         esi
106:          pop         ebx
00401312   pop         ebx
107:          mov         esp, ebp
00401313   mov         esp,ebp
108:          pop         ebp                 ; epilog end
00401315   pop         ebp
109:          // return to caller function(do not use ret 10h)
110:          ret
00401316   ret
--- No source file  --------------------------------------------------------------
00401317   int         3
虽然复制过来有点错位,不过不难看出,我们的汇编代码被照搬了,用曾泰的话说:丝毫不差.

?

这就意味着,我们只要保证寄存器调用后符合调用约定,此外就可以为所欲为了

我不希望大家堕入哲学的深渊,不过我只能用这样的语句来描述:

什么都要自己做和什么都能自己做,往往是一回事,这就是辩证.

?

还记得前面说的fastcall中变量被存来存去吗,嘿嘿,用naked就可以避免,实现真正的fastcall

此外,一些常用的内联汇编的函数,也可以用这种方式输出,当然了,需要具备一定的汇编语言基础

再如,我们可以连堆栈都不构造,直接使用esp代替ebp,即省去prolog和epilog.

?

文章似乎到这里就结束了,那么知道这些有什么用呢?如果你有这样的想法,那就再好不过了,通常人知道了也就知道了

能有这样的想法,说明有学以致用的习惯或者说性格.

那么,我们就试着提出几个用处吧:

?

1. 提升调试程序的能力,更深入的找出BUG的根源,并得出更深一层的解决方法

比如莫名其妙的崩溃,缓冲区溢出,还有这种:

\

?

2.提升程序开发水平,比如常用的strcpy,memset等等函数,应该自己写,更好的发挥机器的性能,

法国一个科学家,用一台普通的计算机计算圆周率,其效率居然可以与一台超级计算机相媲美

单靠数学是无法完成这样的工作的.他必然对计算机的指令架构十分熟悉

?

3.对逆向和软件加密有一定程度的认知

比如单单知道eax作为返回值这一点,你就该认识到,仅用一个函数去做授权验证是很容易绕过的!

既然能想到绕过别人程序的办法,就应该想出防止别人简单绕过的方案

实际的软件加密和破解虽然并没有那么简单,不过起码有这样的一个认知,

?

说起破解,我到还想再废话一下,有些学习破解的朋友,反汇编出来,修改指令,就认为天下的软件就这么干就能破解了

对软件行业充满了失落感,那么我们来举个例子:

使用OD载入一个程序