前一篇演示了在堆上分配空间,生成出可执行代码,并执行之。一般来说可执行代码都是在代码段里的,在堆上和在数据段里都的不是“正常编译”得到的代码。这次就来演示一下把代码放到数据段的例子吧~
代码如下。跟前一篇一样,把对应的机器码和汇编都写在注释里,方便理解。
#include <stdio.h>
typedef int (*puts_ptr)(const char*);
typedef void (*myfunc_ptr)(puts_ptr);
/*
void foo(puts_ptr p) {
p("greetings from generated code!");
}
*/
/* code as string in data section:
offset | bytes (in hex) | mnemonics
00 | 55 | push EBP
01 | 8BEC | mov EBP, ESP
03 | E8 00000000 | call next instruction
08 | 58 | pop EAX
09 | 83C0 0D | add EAX, 0D
12 | 50 | push EAX
13 | FF55 08 | call dword ptr [EBP + 8]
16 | 83C4 04 | add ESP, 04
19 | 5D | pop EBP
20 | C3 | ret
*/
int main() {
myfunc_ptr pMyfunc;
puts_ptr pPuts = &puts;
const char* code = "\x55\x8B\xEC\xE8\x00\x00\x00\x00"
"\x58\x83\xC0\x0D\x50\xFF\x55\x08"
"\x83\xC4\x04\x5D\xC3"
"greetings from generated code!";
pMyfunc = (myfunc_ptr)code;
pMyfunc(pPuts);
return 0;
}
可以看到,这个例子的关键就是源码里的字符串字面量——那堆奇怪的\xNN和后面接着的问候语。不要忘记C/C++里相邻的字符串字面量会被编译器合并为一个字符串常量。Windows上的
PE文件区分代码段(一般在.text section里)与数据段(一般在.data section和.rdata section;后者是只读数据段)里,而这个字符串字面量就会被安置在数据段中。
这个例子里放在数据段的代码的结构与前一篇放在堆上的代码基本一样,也是把代码与其需要的数据混在一起,前面是代码,字符串数据紧跟在代码后。
不同的是:前一篇生成的代码中第3条指令是一个push imm32,压入栈的是直接量;这个直接量是在生成代码时通过相对偏移量计算出来的。这个例子则使用了一个小trick来获取基地址,直接在代码中计算push的值,使用压入栈指令是push EAX。这个trick是以下的指令序列:
offset | bytes (in hex) | mnemonics
03 | E8 00000000 | call next instruction
08 | 58 | pop EAX
09 | 83C0 0D | add EAX, 0D
首先是一条call imm32指令,参数是0x00000000。这条指令的语义是:把下一条指令的地址作为返回地址压入栈,并且跳转到下一条指令的地址+0x00000000的位置。注意这条指令的32位直接量是2的补码,是带符号的。执行这条指令之后,我们就让CPU把call指令的下一条指令的地址压到了栈上,这样就可以知道“基地址”是多少了。
接下来是pop EAX,把刚才压到栈上的“返回地址”弹出来,赋值给EAX。
然后是add EAX, imm8。数一下指令的长度,可以知道要显示的字符串距离pop EAX指令的偏移量是13,也就是0x0D;所以把前面得到的“基地址”加上这个偏移量,就得到了要显示的字符串的起始地址。
后面的指令序列基本上就跟前一篇的一样了,不用多解释。
这种获取地址的trick在可重定位的代码(relocatable code)中很常见。例如编译器生成一个DLL的时候,它不知道实际运行的时候装载器会把DLL image装载到什么地址,所以里面跟地址相关的代码都得编译为基地址+偏移量的形式。基地址的获取经常就是用类似的trick来完成的。
前一篇提到了Windows Vista和Windows 7上的DEP,它对数据段的可执行性也有限制:数据段内存被认为是
不可执行的。所以这个例子在上述两种操作系统(和对应的Server版)里直接编译运行会出错。这些系统认为可执行代码就应该保存到代码段,所以上例中放在字符串中的代码也应该移到代码段中。
在堆上和数据段里放代码有什么意思呢?就像有些时候C的switch...case语句会被编译为基于表的跳转,而这个跳转表就夹杂在指令序列中一样,代码和数据其实没有明显的界限——冯·诺依曼体系结构的机器上,存储器既可以用于保存代码,也可以用于保存数据;代码就是数据,数据可以看作代码来执行。这篇和前一篇的demo只是演示了这一点而已。引申开来,有时候我们会希望根据运行时的某些特定条件来动态生成些效率较高的、特化的代码,而不是使用通用但效率较低的代码,这个时候就需要用到动态代码生成技术。生成的代码放在内存里,如果不能执行那就不好玩了。这两个demo演示了动态生成代码是可执行的。
下次再写咯……喝茶去~
分享到:
相关推荐
在这个"对SQLite数据库增,删,改,查操纵的一个demo"中,我们将探讨如何利用C#进行基本的数据库操作。 首先,我们需要引入SQLite相关的库。在C#中,我们可以使用System.Data.SQLite库,这是一个官方的.NET框架...
总之,`DrawRect` Demo代码实例为我们提供了一个很好的平台,帮助我们学习和理解如何在iOS应用中使用`drawRect:`方法进行图形绘制,尤其是矩形的绘制。通过深入研究和实践,开发者可以创造出更多美观且功能丰富的...
- **Dexmaker**: Google提供的一款用于Android测试的库,它可以在运行时生成和注入Dex代码,实现对Dalvik虚拟机的静态Hook。 2. **动态Hook**: - **Xposed Framework**: 这是Android上最著名的动态Hook框架,它...
JavaScript是Web开发中的核心语言,它允许开发者在用户浏览器上运行代码,实现页面动态更新、事件处理和与服务器的实时通信等功能。在这个Demo中,JavaScript可能被用来处理游戏逻辑、动画效果、用户输入响应以及与...
总结来说,"android拖动布局Demo"是一个关于在Android平台上实现可拖动布局的实例,主要利用`ViewDragHelper`进行视图拖放操作。它涵盖了触摸事件处理、边界检测以及可能的动画效果,对于想要增强自己应用交互性的...
《C语言求职招聘程序小Demo》是一个展示C语言综合运用能力的项目,它结合了指针、文件操作、键盘输入处理以及数据结构中的链表应用。这个项目旨在帮助求职者或者学习者提升C语言编程技能,同时也能为面试提供一个...
"highlig_spring4"可能表示这是针对Spring 4.x版本的示例,Spring 4.x是一个重要的版本,引入了对Java 8的支持以及其他改进和新特性。 在Spring 4.x中,关键知识点包括: 1. **依赖注入(Dependency Injection, DI...
本文将深入探讨这两种技术,并通过一个简单的AOP demo来阐述它们的使用方法。 首先,让我们了解什么是CGLib。CGLib全称为Code Generation Library,是一个Java字节码操纵库,主要用于为类创建子类,从而实现动态...
"Unity 地形Demo"是一个典型的示例项目,用于展示Unity引擎如何构建和操纵地形。在这个Demo中,我们可以学习到许多关于Unity地形系统的知识点。 首先,地形(Terrain)在Unity中是一个组件,可以通过“GameObject”...
例如,`requestAnimationFrame()`是一个高效的动画框架,它可以保证动画在浏览器重绘之前执行,从而得到流畅的视觉体验。 4. **CSS3变换和过渡**:JavaScript可以配合CSS3的transform和transition属性实现复杂的...
这个Demo可能演示了如何创建一个全向滚动视图,允许用户通过操纵杆控制内容的平移,这对于理解和应用上述知识点非常有帮助。你可以通过分析和运行这个示例来更深入地了解操纵杆地图的实现细节。
在Flash环境中,通常会有一个主文件(如`.fla`文件)包含了所有的源代码、图形和动画,而"PPL"可能是这个主文件的别名,或者是项目中某个关键组件的简称。如果"PPL"是一个单独的文件,那么它可能包含了游戏的特定...
【标题】"jpct_ae的3D模型demo"是一个基于Java平台的3D图形库jpct_ae的实际应用展示,它着重展示了如何在应用程序中加载、操纵和交互3D模型。jpct_ae(Java Professional Computer Graphics - Advanced Edition)是...
在描述中,作者提到编写了一个DEMO,DEMO通常是指演示程序或示例代码,它提供了一个快速理解功能和工作原理的方式。作者可能通过这个DEMO展示了如何利用MATLAB与CST之间的接口进行交互,实现自动化建模和仿真过程。...
这个demo很可爱的,是用鼠标和键盘来操纵观察一个小萝莉的3D立体模型。 【Visual C++】游戏开发笔记之【浅墨DirectX提高班】系列博文 配套详细注释源码之十 源码配套博文 《【Visual C++】游戏开发笔记四十二 浅墨...
在某些情况下,开发者可能需要在运行时动态地修改或注入代码到已存在的方法中,以实现特定功能,例如性能监控、日志记录或者行为调整。这就是所谓的“方法注入”或“拦截”。标题中的“c#方法注入替换,独立底层实现...
在文件名列表中,我们看到"Test_PNG",这很可能是DEMO中的一个特定文件,可能是一个源代码文件或者测试用的PNG图像。这个文件可能被用来展示如何在GDI+中加载和处理PNG图像,包括显示、裁剪、缩放、旋转等操作。 ...
浏览器Cookie获取插件Demo是一个用于演示如何在浏览器环境中获取指定URL的Cookie信息的示例项目。这个RAR压缩包中包含了实现这一功能的源代码和可能的文档,让我们深入了解一下相关知识点。 1. **浏览器插件**:...
在Java编程语言中,文件操作和输入/输出流(IO流)是至关重要的部分,尤其在处理数据存储、文件读写以及系统间的通信时。JavaDemo.rar中的内容...通过分析并运行JavaDemo中的代码,你可以更深入地理解和应用这些概念。
在使用百度地图API时,为了防止滥用并确保服务稳定,开发者需要申请一个API密钥。这个DEMO中包含了密钥,意味着可以直接运行,但需要注意的是,公开的密钥可能会带来安全风险,因此在实际项目中,应当妥善保管自己的...