先分享一个案例:

 #include <stdio.h>

 __declspec(naked) void Test()
{
int x;
x = ;
__asm ret;
} int main(int argc, char* argv[])
{
int x = ;
Test();
printf("%d\n",x);
return ;
}

猜猜输出什么?输出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 代码就应将该寄存器的值存储到该变量的堆栈位置中。 例如:

     // nkdfastcl.cpp
    // compile with: /c
    // processor: x86
    __declspec(naked) int __fastcall power(int i, int j) {
    // calculates i^j, assumes that j >= 0 // prolog
    __asm {
    push ebp
    mov ebp, esp
    sub esp, __LOCAL_SIZE
    // store ECX and EDX into stack locations allocated for i and j
    mov i, ecx
    mov j, edx
    } {
    int k = ; // return value
    while (j-- > )
    k *= i;
    __asm {
    mov eax, k
    };
    } // epilog
    __asm {
    mov esp, ebp
    pop ebp
    ret
    }
    }

编写 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 ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
} /* Function body */
__asm { /* epilog */
mov esp, ebp
pop ebp
ret
}
}

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

裸函数naked解析的更多相关文章

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

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

  2. 字串符相关 split() 字串符分隔 substring() 提取字符串 substr()提取指定数目的字符 parseInt() 函数可解析一个字符串,并返回一个整数。

    split() 方法将字符串分割为字符串数组,并返回此数组. stringObject.split(separator,limit) 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如 ...

  3. Generator函数语法解析

    转载请注明出处: Generator函数语法解析 Generator函数是ES6提供的一种异步编程解决方案,语法与传统函数完全不同.以下会介绍一下Generator函数. 写下这篇文章的目的其实很简单 ...

  4. [教程]Delphi 中三种回调函数形式解析

    Delphi 支持三种形式的回调函数 全局函数这种方式几乎是所有的语言都支持的,类的静态函数也可以归为此类,它保存的只是一个函数的代码起始地址指针( Pointer ).在 Delphi 中声明一般为 ...

  5. windows窗口过程函数名词解析

    windows窗口过程函数名词解析 LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) 1. LR ...

  6. 两个实例轻松理解js函数预解析

    js函数预解析 例子1: 先上一段代码,看看能不能写出最终的执行结果. console.log(a); var a = 1; console.log(a); function a(){ console ...

  7. mask-rcnn代码解读(六):resize_image()函数的解析

    我已经根据resize_image()函数的解析对原图像与resize图像进行了解析, 若有读者想对原图像与目标图像不同尺寸验证,可根据以下代码,调整函数参数, 其细节如下: import cv2 a ...

  8. Python中sort和sorted函数代码解析

    Python中sort和sorted函数代码解析 本文研究的主要是Python中sort和sorted函数的相关内容,具体如下. 一.sort函数 sort函数是序列的内部函数 函数原型: L.sor ...

  9. python+opencv->边缘提取与各函数参数解析

    前情提要:作为刚入门机器视觉的小伙伴,第一节课学到机器视觉语法时觉得很难理解, 很多人家的经验,我发现都千篇一律,功能函数没解析,参数不讲解,就一个代码,所以在此将搜集的解析和案例拿出来汇总!!! 一 ...

随机推荐

  1. cvs的规范以及介绍(转)

    原文链接:http://blog.csdn.net/zlzlei/article/details/9236403 1. CSV的全称是叫Comma Separated Value 2. CSV的MIM ...

  2. 解决Jenkins 中无法展示 HTML 样式的问题

    问题 将本地的jmeter脚本部署到Jenkins上时,可以运行成功也可以在本地生成正确的HTML.但在Jenkins中查看HTML report时内容显示不出来. because the docum ...

  3. 最终还是迁移到github

    作为全球最大的程序员同性交友社区,github pages 吸引了我 为了有一个更好的博客的写作环境 将会把内容逐渐迁移到 github.io 地址 zongxiao.github.io 新的文章也会 ...

  4. python 读写西门子PLC 包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC

    本文将使用一个gitHub开源的组件技术来读写西门子plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作 nu ...

  5. lmdb数据格式

    http://deepdish.io/2015/04/28/creating-lmdb-in-python/ https://lmdb.readthedocs.org/en/release/ http ...

  6. SVM大致思路整理

    (一)线性可分 我们忽略建立目标函数的过程,直接写出目标函数. 原问题: 首先,我们得到了目标函数: 这是一个凸优化问题,直接可以用软件可以求解: 对偶问题: 原问题根据一系列的变换,可写成: 满足某 ...

  7. chrom调试javascript

    上面的文章已经大致介绍了一下console对象具体有哪些方面以及基本的应用,下面简单介绍一下如何利用好chrome控制台这个神器好好调试javascript代码(这个才是我们真正能用到实处的地方) 1 ...

  8. Flash 开发环境搭建和文字滚动效果实例

    Flash 开发环境搭建和文字滚动效果实例 一.Flash 开发环境搭建 Flash发布的时候可以将资源(即将库中的元件)集成到swf运行文件中.Flash没有代码自动输入补全功能,因此需要一个英文一 ...

  9. codevs 计算器的改良

    #include<iostream> #include<cctype> #include<vector> #include<cstdio> using ...

  10. sublime text 2 卸载与重装

    很多同学使用 sublime text2 的时候,出现一些奇怪的bug,且重启无法修复. 于是,就会想到卸载 sublime text2 再重新安装. 然而,你会发现,重新安装后,这个bug任然存在, ...