hook什么意思_这是什么骚代码!
神秘代码
今天给大家看个有意思的东西!
不仅有意思,还能学到知识。
话题从两行(准确的说是一行)神奇的代码聊起:
// main.c#include int main[] = { 232,-1065134080,26643,12517440,4278206464,12802064,(int)printf };
这是一段C++代码,猜猜看编译运行后,会输出什么?
可能,你会问:这TM连main函数都没有,能编译成功?
还真能!
咱们分别在Windows平台下的Visual Studio和Linux平台下的 g++ 进行编译,然后分别执行看看效果:
Windows下:
Linux下:
不仅能编译成功,还能正常运行,在Windows上输出了一个MZ,在Linux上输出了一个ELF。
熟悉PE文件格式的同学可能知道,MZ是PE文件开头的标志,另外,ELF也是Linux上的可执行文件开头的标志。
也就是说:上面这行代码执行后,把所在可执行文件头部的字符串给打印出来了!
反汇编真相
看到这里,你可能有两个问题:
- 为什么没有main函数还能通过编译?
- 为什么会输出这么一串信息?
对于第一个问题,相信大家应该也猜到了个八九不离十。虽然代码中没有main函数,但是有一个main数组啊!会不会跟它有关系?
是的没错,对于编译器而言,函数也好,变量也好,最终都处理成了一个个的符号Symbol,而编译器并没有区分这个符号是来自一个函数还是一个数组。所以,我们用一个main数组,骗过了编译器。
也就是说:编译器把main数组当成了main函数,把main数组中的数据当成了main函数的函数体指令。
而要回答第二个问题,那就得看下这个main数组中的这一段奇怪的数字,到底是一段什么样的代码?
将main数组中的数值转换成16进制看看,按照一个int变量占4个字节对齐:
再进一步,使用反汇编引擎看看这段16进制数据是什么指令?
接下来,咱们逐条分析这些指令。
call $+5
这是一条非常重要的指令,请记住:call指令是在执行函数调用,执行call指令的时候,会将下一条指令的地址压入线程的栈顶,用于函数返回时取出找到回去的路,那下一条是谁?就是下面的pop eax这条指令,所以执行这个call指令时,会把下面那个pop eax指令的地址压入栈顶。
再者,call后面的目标地址是$+5,也就是这条call指令地址+5个字节的地方,同样是下面那条pop eax指令的地址,所以call的目标函数就是紧接着的下面pop eax指令开始的地方。
那这么费劲执行这个call $+5的意义何在?其实就是为了获取当前这段代码所在的内存空间地址,但是又没有办法直接读取指令寄存器EIP的值,所以借助一个call,把这段代码的地址压入到堆栈中,随后再取出来就能知道这段代码被放置在内存中哪个地址在执行了。
这个手法,是黑客编写shellcode的惯用伎俩。
pop eax
注意,执行到这里的时候,线程的栈顶存放的就是这条指令所在的位置,是上面那条call指令导致的结果。
接着,pop eax,将栈顶存放的这个地址取出来,放到eax寄存器中。现在eax中存放的就是当前指令的内存地址了。
add eax, 13h
上面费这么大劲拿到了这个地址有什么用呢?别急,看这条指令,给它加了13h,也就是十进制的19,回头看看main数组那个十六进制字节表,加了19后,正好是main数组最后一个元素所在的位置——里面存放了printf函数的地址。
所以,截止到这里,前面这三条指令的目的就是为了能拿到printf函数的地址。
push 400000h↵↵拿到printf函数以后,开始调用。这里给printf传了一个参数:0x00400000,也就是要打印的字符串地址。
mov edi, 400000h↵↵这里同样是在给printf函数传参,这里和上面那条,一个通过堆栈传参,一个通过寄存器传参数,是为了同时兼容Windows平台和Linux x64平台上的函数调用约定。
而之所以传递的字符串地址是0x00400000,是因为刚好,这个数字是两个平台上可执行文件加载的默认基地址。
Windows:
Linux:
(gdb) x /16c 0x004000000x400000: 127 '\177' 69 'E' 76 'L' 70 'F' 2 '\002' 1 '\001' 1 '\001' 0 '\000'0x400008: 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000' 0 '\000'
call dword ptr [eax]
还记得前面eax存储的是main数组的最后一个格子的地址,这个格子里面存放的是printf函数的地址。
于是,通过一个指针调用call,来调用printf,完成打印输出。
pop eax
函数调用完了,得进行堆栈平衡,前面传参压栈了,这里就得弹出来。
retn
注意这个retn指令,retn指令和call指令对应,call用于调用函数,将返回地址压栈,而retn指令则将栈顶的数据弹出来作为返回地址,跳回去执行。
还记得吗,现在这段代码是处于被第一个call指令调用的上下文中的,正常情况下,执行retn是不是应该返回到call指令后面?那岂不是又回去pop eax走一遍乱了套了?但注意,现在栈顶的那个返回地址已经提前被pop出来了(第二行那个pop eax),那现在执行retn,取出来的栈顶数据又是什么呢?
这个数据就是线程执行到整个main函数最开始的时候,栈顶保留的调用main函数的调用者的返回地址。所以这个retn不是返回到第一个call后面,而是返回到了上一级调用main函数的的那个地方。
至于具体是谁在调用main函数,这就不是这篇文章的重点了,属于Linux和Windows上各自的C/C++运行时库CRT函数的范畴。
到这里,你应该就能明白,这个程序是如何运行起来的,以及,为什么会有那样的输出信息。
几个注意事项
- 首先,为了能够顺利通过编译,在Linux上,需要使用 g++ 而不是gcc进行编译,因为对main这个全局变量初始化时,C语言规定必须是常量,而不能是动态确定的(最后那个printf函数地址就是动态的),同时还得加上 -fpermissive 编译选项。
- 需要关闭模块的随机加载功能。现代操作系统为了抵抗安全攻击,可执行文件的加载基地址都进行了随机化,防止被猜测,而这段代码能够正常运行的前提是可执行文件加载基址是0x00400000。不能随机化,所以需要通过编译器来关闭。
- 最后,根据前面的分析其实也知道了,其实程序把main数组中的数据当成了代码在执行。在现代操作系统的安全性保护下,默认情况下是拒绝执行数据所在的内存页面的,因为这些内存页面只有读写权限,而没有可执行权限,这一安全机制叫DEP/NX。所以为了正常运行,需要把这个关闭。对于g++,添加 -z execstack 编译选项即可。
总结
其实这段代码的思路并非我的原创,在国外有一个国际C语言混乱代码大赛(IOCCC, The International Obfuscated C Code Contest)。这个比赛的特点就在于写最骚的代码,实现最奇葩的效果,其中就有这样的获奖案例。
后来,国内一个大牛也原创了自己的版本,参考链接:
https://blog.csdn.net/masefee/article/details/6606813
不过,这个版本仅适用于Windows平台,我在此基础之上,又改了现在这个版本,同时支持Windows和Linux平台。
这段代码本身没有任何意义,不具备实用价值,但透过代码去研究代码和程序背后执行的底层原理,了解CPU如何调用函数、传递参数,跳转,操作堆栈,这些才是这篇文章的意义所在。
给大家留个思考题,下面这行代码能正常运行起来吗,运行起来又做了什么呢?
int main[] = {0xC3};
欢迎评论区留言探讨!
往期推荐
打钱!我的数据库被黑客勒索了!
黑客爱用的HOOK技术大揭秘!
安全软件群雄混战史
hook什么意思_这是什么骚代码!相关推荐
- flask与js交互的示例代码_Frida Java Hook 详解(安卓9):代码及示例(上)
Frida Java Hook 详解(安卓9):代码及示例(上) 前言 1.1 FRIDA SCRIPT的"hello world" 1.1.1 "hello world ...
- 五分钟没有操作自动退出_这又是什么骚操作??5只蚂蚁战略配售基金拟增设B类份额,自动赎回退出!!...
他来了,他来了,这又是什么骚操作??昨天,五只创新未来18个月封闭运作混合型证券投资基金发布联合声明,会为这个战略配售基金安排一个月的退出选择期. 5只创新未来18个月封闭运作混合型证券投资基金发布联 ...
- java代码里的JSON格式怎么写好看_谁会不爱让代码骚里骚气的VSCode扩展插件呢?...
点击上方 "Python人工智能技术" 关注,星标或者置顶22点24分准时推送,第一时间送达 来自:公众号 读芯术 | 编辑:真经君 码农真经(ID:coder_experienc ...
- jar 工程我怎么在网页上url访问某一个方法_搜狗用这个骚技术,把百度逼上了绝路。。。...
点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] 前几天在百度搜索的时候,一不小心误点搜索候选词,给我跑到搜狗搜索里面去了,索性花了点时间分析一下这其中的猫腻,不看不知道,一看吓一跳. 在 ...
- frida hook java 函数_使用 Frida 来 Hook Java 类中的构造函数(构造函数带重载),获取解密后的js脚本...
一个APP使用了Auto.js 的加密脚本.我们的任务是将其加密脚本进行解密并dump出来.在 https://www.52pojie.cn/thread-1112407-1-1.html 一文中, ...
- hook监控限制_**CodeIgniter通过hook的方式实现简单的权限控制
根据自己的实际情况,需要两个文件,一个是权限控制类,Acl,另外一个是权限配置的文件acl.php放在了config这个目录下. Acl这个类放在了application/hook/acl.php.通 ...
- python hook pc微信_一起来用python玩一波微信呀 | 防撤回, 好友分析, 聊天机器人~...
原文链接一起来用python玩一波微信呀 | 防撤回, 好友分析, 聊天机器人~mp.weixin.qq.com 导语 众所周知,前段时间微信彻底关闭了网页版微信登录入口.于是一大波基于itchat ...
- python hook pc微信_微信机器人之PC微信hook
微信机器人的实现有三种:web,app和exe.其中web很多账号受限登录不了,而hook app的话需要使用xposed则会封号,所以现在大部分机器人都是基于PC微信. 先实现一下最基本的机器人的功 ...
- java hook 和反射_反射基本概念与Class(四):Hook技术动态编程
背景介绍 很多时候系统处于安全考虑,将很多东西对外隐藏,而有时我们偏偏又不得不去使用这些隐藏的东西.甚至,我们希望向系统中注入一些自己的代码,以提高程序的灵活性.刚好有这么一种特殊的回调模式,Hook ...
最新文章
- python经典例题图形_Python 入门经典100实例:实例23 菱形
- Meta为元宇宙建全球最快AI超算,1.6万个A100 GPU,英伟达都赚麻了
- 原始套接字SOCK_RAW
- swift写的摇骰子程序,开源了
- mysql 分组占比_含泪整理MySQL索引
- GitHub 的前世今生
- java数据库实例_选择数据库实例
- 华为大佬:做一个快乐的程序员,而不是码农
- Css、javascript、dom(一)
- IE游览器的扩展事件
- javascript遍历对象属性和方法
- [JNI]开发之旅(5)访问c/c++函数
- 【好用的Mac分屏软件】Magnet for Mac 2.3
- Flash的破解版按装
- 使用USBCAN通讯(转载)
- 鲸会务会议管理系统线上会议邀约、推广、获客、互动一站式解决方案
- BPF入门1:BPF技术简介
- 怎么让照片变年轻_如何用ps把人变年轻水嫩
- JavaScript实现警告框
- 【号外】支付宝小程序,惊呆我的小伙伴
热门文章
- One Order distribution logic issue - automatic BDOC creation and sent to Middleware outbound queue -
- which kinds of error message will prevent business transaction save
- ABAP string函数一览
- 使用DOM Breakpoints找到修改属性的Javascript代码
- idea JDK安装与配置
- 本地缓存需要高时效性怎么办_详解微信小程序缓存--缓存时效性
- python数据表_第1关:了解python数据表操作
- python的for语句中i未被定义_python – PyLint:使用可能未定义的循环变量警告
- unity ui插件_用Unity制作GalGame/视觉小说游戏的模型素材与插件推荐
- 电子设计常用总线--QSPI