先分享一个案例:

 1 #include <stdio.h>
 2
 3 __declspec(naked) void Test()
 4 {
 5     int x;
 6     x = 3;
 7     __asm ret;
 8 }
 9
10 int main(int argc, char* argv[])
11 {
12     int x = 1;
13     Test();
14     printf("%d\n",x);
15     return 0;
16 }

猜猜输出什么?输出3,而不是1。

看下反汇编代码:

有疑问先留着。下面讲解下naked:

MSDN中关于naked关键字的介绍:

For functions declared with the naked attribute, the compiler generates code without prolog and epilog code. You can use this feature to write your own prolog/epilog code sequences using inline assembler code. Naked functions are particularly useful in writing virtual device drivers. Note that thenaked attribute is only valid on x86, and is not available on x64 or Itanium.

我们知道VC++和gcc都支持naked函数,即所谓的“裸函数”,对于使用 naked 特性声明的函数,编译器将生成编码,而无需 prolog 和 epilog 代码。而一般性函数,编译器会主动加上很多prilog和epilog代码,还会做一些优化,有些是赘余的。而使用内联汇编,可以写完全按自己意愿跑的函数。可以使用此功能来编写使用汇编程序代码的自己的 prolog/epilog 代码顺序。 裸函数尤为用在编写虚拟设备驱动程序。请注意 naked 特性仅适用于 x86和ARM,并不用于 x64 。关于自己编写prolog 和 epilog 代码,在后面有讲到。

VC++的声明语法:__declspec(naked)

gcc的声明语法:__attribute__((naked))

因为编译器不会生成入口代码和退出代码,所以写naked函数的时候要分外小心。进入函数代码时,父函数仅仅会将参数和返回地址压栈,亦即只有esp寄存器和eip寄存器会发生变化。

一般来说,使用naked函数时需要注意以下问题:(以VC++编译器为例)

1、函数必须显式返回。

一般通过__asm ret的内嵌汇编指令返回。

2、不可以通过任何方式使用局部变量。

若声明一个局部变量,并在代码中为其赋值,则会更改父函数中相应位置的局部函数的值。

3、只能通过esp引用参数。

因为子函数继承了父函数的ebp寄存器,所以只能通过esp引用参数。

4、naked 属性仅与函数的定义相关,不能在函数原型中指定。不能用于函数指针,不能用于数据定义。

官方给出的naked的规则限制:

  • 不允许使用 return 语句。

  • 不允许结构化异常处理和 C++ 异常处理构造,因为它们必须在堆栈帧中展开。

  • 出于同一原因,禁止任何形式的 setjmp

  • 禁止使用 _alloca 函数。

  • 若要确保局部变量的初始化代码不在 prolog 序列之前出现,函数范围中不允许存在初始化的局部变量。 具体而言,函数范围中不允许有 C++ 对象的声明。 但是,嵌套的范围中可能有初始化的数据。

  • 不建议使用帧指针优化(/Oy 编译器选项),但会自动为裸函数将其取消。

  • 不能在函数词法范围中声明 C++ 类对象。 但是,可以在嵌套的块中声明对象。

  • 在使用 /clr 进行编译时,将忽略 naked 关键字。

  • 对于 __fastcall 裸函数,只要 C/C++ 代码中存在对某个寄存器参数的引用,prolog 代码就应将该寄存器的值存储到该变量的堆栈位置中。 例如:

     1 // nkdfastcl.cpp
     2 // compile with: /c
     3 // processor: x86
     4 __declspec(naked) int __fastcall  power(int i, int j) {
     5    // calculates i^j, assumes that j >= 0
     6
     7    // prolog
     8    __asm {
     9       push ebp
    10       mov ebp, esp
    11       sub esp, __LOCAL_SIZE
    12      // store ECX and EDX into stack locations allocated for i and j
    13      mov i, ecx
    14      mov j, edx
    15    }
    16
    17    {
    18       int k = 1;   // return value
    19       while (j-- > 0)
    20          k *= i;
    21       __asm {
    22          mov eax, k
    23       };
    24    }
    25
    26    // epilog
    27    __asm {
    28       mov esp, ebp
    29       pop ebp
    30       ret
    31    }
    32 }

编写 Prolog/Epilog 代码的注意事项

堆栈帧布局:

此示例显示了可能出现在 32 位函数中的标准 prolog 代码:

push        ebp                ; Save ebp
mov         ebp, esp           ; Set stack frame pointer
sub         esp, localbytes    ; Allocate space for locals
push        <registers>        ; Save registers

localbytes 变量表示局部变量堆栈上所需的字节数,<registers> 变量是表示要保存在堆栈上的寄存器列表的占位符。 推入寄存器后,您可以将任何其他适当的数据放置在堆栈上。 下面是相应的 epilog 代码:

pop         <registers>   ; Restore registers
mov         esp, ebp      ; Restore stack pointer
pop         ebp           ; Restore ebp
ret                       ; Return from function

堆栈始终向下增长(从高内存地址到低内存地址)。 基指针 (ebp) 指向 ebp 的推入值。 本地区域开始于 ebp-4。 若要访问局部变量,可通过从 ebp 中减去适当的值来计算ebp 的偏移量。

__LOCAL_SIZE :

编译器提供符号 __LOCAL_SIZE 以用于函数 prolog 代码的内联汇编程序块中。 此符号用于在自定义 prolog 代码中的堆栈帧上为局部变量分配空间。

编译器确定 __LOCAL_SIZE 的值。 其值是所有用户定义的局部变量和编译器生成的临时变量的总字节数。 __LOCAL_SIZE 只能用作即时操作数;它不能在表达式中使用。 不得更改或重新定义此符号的值。 例如:

mov        eax, __LOCAL_SIZE           ;Immediate operand--Okay
mov        eax, [ebp - __LOCAL_SIZE]   ;Error

以下包含自定义 prolog 和 epilog 序列的裸函数的示例在 prolog 序列中使用 __LOCAL_SIZE 符号:

// the__local_size_symbol.cpp
// processor: x86
__declspec ( naked ) int main() {int i;int j;__asm {      /* prolog */push   ebpmov      ebp, espsub      esp, __LOCAL_SIZE}/* Function body */__asm {   /* epilog */mov      esp, ebppop      ebpret}
}

最初的案例现在看起来简单多了,编译器不主动加prolog 和 epilog 代码,子函数用父函数的ebp,所以这里修改了main函数的栈,后面结果出错了。后面的ret是必须写蛤,要自己维持栈平衡。

裸函数naked解析相关推荐

  1. c++裸函数naked解析

    先分享一个案例: 1 #include <stdio.h>2 3 __declspec(naked) void Test()4 {5 int x;6 x = 3;7 __asm ret;8 ...

  2. 【逆向知识】裸函数(Naked函数)

    1 说明 指定裸函数编写的函数,编译器生成不带任何多余代码. 利用此功能,可以使用内联汇编程序代码编写自己的 prolog/epilog 代码序列. 裸函数对于编写虚拟设备驱动程序特别有用. 2 练习 ...

  3. c语言裸函数,【逆向知识】裸函数(Naked函数)

    1 说明 指定裸函数编写的函数,编译器生成不带任何多余代码. 利用此功能,可以使用内联汇编程序代码编写自己的 prolog/epilog 代码序列. 裸函数对于编写虚拟设备驱动程序特别有用. 2 练习 ...

  4. _declspec(naked) 使用(裸函数)

    最近学习驱动开发,在写绕过inline hook的代码时,有个问题困扰了我一天,最后发现原来是在内嵌汇编时,没有使用_declspec(naked)导致的,看来是偶的基础知识掌握的不牢固啊(得补一下了 ...

  5. 【Win32汇编】__declspec(naked)裸函数

    使用 __declspec(naked) 定义的函数,编译器只会负责参数压栈.执行CALL指令,和释放参数的内存(堆栈平衡),除此之外啥也没有,比如提升堆栈,寄存器的保存和恢复,或者是函数返回,这些都 ...

  6. 裸函数 __declspec(naked)

    在C语言转化成汇编的过程中,编译器会自动处理堆栈,比如以下代码,即使add函数里面一条语句都没有,但是编译器在编译过程中 还是生成了很多指令(00F11380 - 00F1139C) 用于处理堆栈 v ...

  7. 裸函数 __declspec(naked),C语言是怎么变成汇编的,用裸函数加汇编实现一个最简单的加法函数

    裸函数: 普通函数会自动生成实现堆栈提升.堆栈的缓存区建立.堆栈平衡和函数返回的汇编代码,裸函数不会自动生成任何代码,全部要自己手动写汇编代码. 裸函数定义:__declspec(naked) 加汇编 ...

  8. c语言裸函数,裸函数的规则和限制

    裸函数的规则和限制 11/04/2016 本文内容 Microsoft 专用 以下规则和限制适用于裸函数: return 不允许使用该语句. 不允许结构化异常处理和 C++ 异常处理构造,因为它们必须 ...

  9. 语言中拟合函数 计算aic_Go语言函数深度解析(中)

    上回函数深度解析给大家聊了一些函数的基本知识,不知道还有没有人记得,不记得赶紧回去复习! 他们是 go语言中函数的基本原理 单/多个同/不同类型参数 单/多个同/不同类型返回值 值传递,引用传递 函数 ...

最新文章

  1. PL/SQL Developer远程访问Oracle数据库
  2. unittest单元测试框架—基本实例
  3. python PyQt5中文教程☞【第十节】PyQt5绘图(绘制文本drawText()、画点drawPoints()、设置颜色、QPen(画笔)绘制线条、QBrush(笔刷)绘制纹理
  4. SAP Spartacus使用cxComponentWrapper测试MiniCart
  5. mysql以user1登录_在mysql中创建用户后不能本地登录的解决方法
  6. 将整张网页存成png图片
  7. Oracle 19c和20c新特性最全解密
  8. DPG图片压缩技术和webp图片格式
  9. 简书吐槽大会|山东被曝大量不合格疫苗
  10. LIRe 源代码分析 5:提取特征向量[以颜色布局为例]
  11. 【转】Javabyte[]数组和十六进制String之间的转换Util------包含案例和代码
  12. C# 设置和获取一个字节的某一位的值的方法 .
  13. 240.搜索二维矩阵II
  14. MATLAB 谐波 模块,simulink 谐波分析_matlab谐波分析_simulink中sign模块(6)
  15. 360浏览器极速模式对iframe的支持
  16. 简单说一下寄存器寻址
  17. Python库详解。python有那些库你都知道了嘛?
  18. 记win10家庭版系统C:\Users用户名中文改英文 详细教程
  19. 2015年华为实习生机试样题(记票统计,计算麻将的番数,Word Maze(单词迷宫))
  20. 当滑雪这项世界最古老的运动遇上AI

热门文章

  1. 海力士芯片 HY57V561620FTP-H 的内存容量问题
  2. bzoj 3999: [TJOI2015]旅游(树链剖分)
  3. apfs扩容_向 APFS 文件系统转进:iOS 10.3 为 iPhone 变相扩容存储空间
  4. 《爬虫爬 wallhaven.cc壁纸》
  5. (六)IT行业名博,你不知道的都在这里!!!
  6. 下列属于计算机网络主要性能指标的有( ),下列属于计算机网络性能指标的有A.速率B.带宽C.时延D.误码率...
  7. Chapter 3 (Determinants): Cramer‘s rule, volume, and linear transformations (克拉默法则、体积和线性变换)
  8. operate mow 3.15
  9. 平面设计实验一 新建文件和格式模式转换
  10. Contract Verify and Publish