zoukankan      html  css  js  c++  java
  • 裸函数

    概述

    版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。2019-09-24 00:36。
    作者By-----溺心与沉浮----博客园

      _declspec(naked)修饰可以生成一个“裸”函数, 使用后C编译器将生成不含函数框架的纯汇编代码,裸函数中什么都没有,所以也不能使用局部变量,只能全部用内嵌汇编实现。

    裸函数的定义

    1 void __declspec(naked) Function()  
    2 
    3 {
    4         ...
    5 }
      _declspec(naked) 的介绍:
      _declspec(naked),就是告诉编译器,在编译的时候,不要优化代码,通俗的说就是,没代码,完全要自己写
      比如:
    1 #define NAKED __declspec(naked)
    2 
    3 void NAKED code(void)
    4 {
    5     __asm
    6     {
    7         ret
    8     }
    9 }
      使用__declspec(naked)关键字定义函数:
      1,使用 naked 关键字必须自己构建 EBP 指针 (如果用到了的话);
      2,必须自己使用 RET 或 RET n 指令返回 (除非你不返回);
     
      _delcspec(naked)用在驱动编写,C语言内嵌汇编完成一些特定功能。
     

    实例

      我们先通过一个C语言中最简单函数,然后观察反汇编代码,看看编译器为我们做了些什么
      编译环境:VmWare Workstation 15 Pro、windows 7、VC++ 6.0 英文版
     1 // xiaoyu1.cpp : Defines the entry point for the console application.
     2 //
     3 
     4 #include "stdafx.h"
     5 
     6 void Plus1()
     7 {
     8     
     9 }
    10 
    11 int main(int argc, char* argv[])
    12 {
    13     Plus1();
    14     return 0;
    15 }
      Plus1()很显然是我们C语言中最简单的一个函数,但是它真的有这么简单吗?我们来看看它的反汇编代码,看看编译器都为这个函数做了些什么。我们在第13行代码处按下F9下一个断点。
      按下F7,F5,进入如下窗口:

       在这个窗口处,右键选择Go To Disassembly,进入我们的反汇编窗口

       点击之后进入如下界面

       看到最左边那一列有个向右的小黄色箭头了吗?这标志我们的程序停在这,黄色箭头处的代码:

    1 00401068   call        @ILT+0(Plus1) (00401005)
      还记得我在前面博文中讲过call吗?https://www.cnblogs.com/Reverse-xiaoyu/p/11470633.html(JMP、CALL、RET指令),call做了两件事情,将下一行地址压栈,并修改EIP的值,我们按下F11,跟进去。
       第一次按下F11我们会看到这行代码
    1 00401005   jmp         Plus1 (00401020)

       再按下F11,如下所示

     1 00401020   push        ebp
     2 00401021   mov         ebp,esp
     3 00401023   sub         esp,40h
     4 00401026   push        ebx
     5 00401027   push        esi
     6 00401028   push        edi
     7 00401029   lea         edi,[ebp-40h]
     8 0040102C   mov         ecx,10h
     9 00401031   mov         eax,0CCCCCCCCh
    10 00401036   rep stos    dword ptr [edi]
    11 00401038   pop         edi
    12 00401039   pop         esi
    13 0040103A   pop         ebx
    14 0040103B   mov         esp,ebp
    15 0040103D   pop         ebp
    16 0040103E   ret

       在最底层,发现事情都不是明面上看到的那么简单,C语言中一个什么都不写的最简单的一个函数,编译器居然为我们生成了这么多的汇编代码,如果你不懂这段,可以移步我的这一篇博文https://www.cnblogs.com/Reverse-xiaoyu/p/11489082.html

       看完了正常的函数之后,我们再去看看裸函数,看看最底层,编译器为它做了些什么,在概要里也说过,编译器什么都不会为它做,自己自生自灭去。
      可能你在反汇编窗口不知道如何退回到编辑界面,按下shift + F5就退回去了
       
     1 // xiaoyu1.cpp : Defines the entry point for the console application.
     2 //
     3 
     4 #include "stdafx.h"
     5 
     6 void __declspec(naked) plus()
     7 {
     8     
     9 }
    10 
    11 void Plus1()
    12 {
    13     
    14 }
    15 
    16 int main(int argc, char* argv[])
    17 {
    18     plus();
    19     return 0;
    20 }
       我们在程序调用处,第18行按下F9下一个断点,然后进入反汇编窗口。
     
     1 --- D:Projectxiaoyu1xiaoyu1.cpp  ---------------------------------------------------------------------------------------------------------------------------
     2 15:
     3 16:   int main(int argc, char* argv[])
     4 17:   {
     5 00401020   push        ebp
     6 00401021   mov         ebp,esp
     7 00401023   sub         esp,40h
     8 00401026   push        ebx
     9 00401027   push        esi
    10 00401028   push        edi
    11 00401029   lea         edi,[ebp-40h]
    12 0040102C   mov         ecx,10h
    13 00401031   mov         eax,0CCCCCCCCh
    14 00401036   rep stos    dword ptr [edi]
    15 18:       plus();
    16 00401038   call        @ILT+10(plus) (0040100f)
    17 19:       return 0;
    18 0040103D   xor         eax,eax
    19 20:   }
    20 0040103F   pop         edi
    21 00401040   pop         esi
    22 00401041   pop         ebx
    23 00401042   add         esp,40h
    24 00401045   cmp         ebp,esp
    25 00401047   call        __chkesp (0040d430)
    26 0040104C   mov         esp,ebp
    27 0040104E   pop         ebp
    28 0040104F   ret
       黄色的小箭头,此时指向第16行 00401038   call        @ILT+10(plus) (0040100f),两步F11进去,程序会直接跑没了,跳到一堆int 3的地方去,因此概述中的2,必须自己使用 RET 或 RET n 指令返回 (除非你不返回);这句话就告诉我们该怎么做了
       我们可以自己写一个内敛汇编,加一个ret进去
     1 // xiaoyu1.cpp : Defines the entry point for the console application.
     2 //
     3 
     4 #include "stdafx.h"
     5 
     6 void __declspec(naked) plus()
     7 {
     8     __asm
     9     {
    10         ret
    11     }
    12 }
    13 
    14 void Plus1()
    15 {
    16     
    17 }
    18 
    19 int main(int argc, char* argv[])
    20 {
    21     plus();
    22     return 0;
    23 }
       在C程序中,一个程序有call就必然有ret,否则它就乱了,找不到它回家的路了,加上这一步之后,调用裸函数的时候它就会正常返回了。

     无参数无返回值的函数框架

     1 void __declspec(naked) plus()
     2 {
     3     __asm
     4     {
     5         //提升堆栈
     6         push ebp
     7         mov ebp,esp
     8         sub ebp,0x40
     9         //保护现场
    10         push ebx
    11         push esi
    12         push edi
    13         //向缓冲区填充数据
    14         lea edi,dword ptr ds:[ebp-0x40]
    15         mov eax,0xCCCCCCCC
    16         mov ecx,0x10
    17         rep stosd  ;rep stos dword ptr es:[edi]
    18         //恢复现场
    19         pop edi
    20         pop esi
    21         pop ebx
    22         //降低堆栈
    23         mov esp,ebp
    24         pop ebp
    25         //返回函数调用前的下一行地址
    26         ret
    27     }
    28 }

     有参数有返回值的函数框架

     1 int __declspec(naked) plus(int x, int y)
     2 {
     3     __asm
     4     {
     5         //提升堆栈
     6         push ebp
     7         mov ebp,esp
     8         sub esp,0x40
     9         //保护现场
    10         push ebx
    11         push esi
    12         push edi
    13         //向缓冲区填充数据
    14         lea edi,dword ptr ds:[ebp-0x40]
    15         mov eax,0xCCCCCCCC
    16         mov ecx,0x10
    17         rep stos dword ptr es:[edi]
    18 
    19         //函数核心功能块
    20         mov eax,dword ptr ds:[ebp+0x8]
    21         add eax,dword ptr ds:[ebp+0xC]
    22 
    23         //恢复现场
    24         pop edi
    25         pop esi
    26         pop ebx
    27 
    28         //降低堆栈
    29         mov esp,ebp
    30         pop ebp
    31         //返回函数调用前的下一行地址
    32         ret
    33     }
    34 }

     带局部变量的函数框架

     1 int __declspec(naked) plus(int x, int y)
     2 {
     3     __asm
     4     {
     5         //提升堆栈
     6         push ebp
     7         mov ebp,esp
     8         sub esp,0x40
     9         //保护现场
    10         push ebx
    11         push esi
    12         push edi
    13         //向缓冲区填充数据
    14         lea edi,dword ptr ds:[ebp-0x40]
    15         mov eax,0xCCCCCCCC
    16         mov ecx,0x10
    17         rep stos dword ptr es:[edi]
    18 
    19         //局部变量入栈
    20         mov dword ptr ds:[ebp-0x4],0x2
    21         mov dword ptr ds:[ebp-0x8],0x3
    22 
    23         //函数核心功能块
    24         mov eax,dword ptr ds:[ebp+0x8]
    25         add eax,dword ptr ds:[ebp+0xC]
    26 
    27         //恢复现场
    28         pop edi
    29         pop esi
    30         pop ebx
    31         //降低堆栈
    32         mov esp,ebp
    33         pop ebp
    34         //返回函数调用前的下一行地址
    35         ret
    36     }
    37 }

    用__declspec(naked)裸函数实现下面的功能

    1 int plus(int x,int y,int z)
    2 {
    3     int a = 2;
    4     int b = 3;
    5     int c = 4;
    6     return x+y+z+a+b+c;
    7 }
     1 int __declspec(naked) plus(int x, int y, int z)
     2 {
     3     __asm
     4     {
     5         //提升堆栈
     6         push ebp
     7         mov ebp,esp
     8         sub esp,0x40
     9         //保护现场
    10         push ebx
    11         push esi
    12         push edi
    13         //填充缓冲区
    14         lea edi,dword ptr ds:[ebp-0x40]
    15         mov eax,0xCCCCCCCC
    16         mov ecx,0x10
    17         rep stos dword ptr es:[edi]
    18 
    19         //局部变量入栈
    20         mov dword ptr ds:[ebp-0x4],0x2
    21         mov dword ptr ds:[ebp-0x8],0x3
    22         mov dword ptr ds:[ebp-0xc],0x4
    23 
    24         //函数核心功能块
    25         mov eax,dword ptr ds:[ebp+0x8]
    26         add eax,dword ptr ds:[ebp+0xc]
    27         add eax,dword ptr ds:[ebp+0x10]
    28         add eax,dword ptr ds:[ebp-0x4]
    29         add eax,dword ptr ds:[ebp-0x8]
    30         add eax,dword ptr ds:[ebp-0xC]
    31 
    32         //恢复现场
    33         pop edi
    34         pop esi
    35         pop ebx
    36 
    37         //降低堆栈
    38         mov esp,ebp
    39         pop ebp
    40         
    41         //返回函数调用前的位置
    42         ret
    43     }
    44 }
  • 相关阅读:
    如何打开visual c++ 6.0的控件工具箱
    点评十大问题网站
    setInterval和clearInterval
    chrome 快捷键
    创意价值链通天塔
    百度前副总裁梁冬《那一些俯视人类欲望的人》
    YUI Compressor Javascript文件压缩工具
    网站服务器通用和专用保护方法比较
    svn log — 显示提交日志信息
    javascript 键值keyCode
  • 原文地址:https://www.cnblogs.com/Reverse-xiaoyu/p/11575682.html
Copyright © 2011-2022 走看看