这是什么骚批代码!

Qt之connect函数使用 阅读详情

神秘代码

大家好 我是周杰伦

今天给大家看个有意思的东西!

不仅有意思,还能学到知识。

话题从两行(准确的说是一行)神奇的代码聊起:

#include <stdio.h>
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 0x00400000
0x400000: 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函数的范畴。

到这里,你应该就能明白,这个程序是如何运行起来的,以及,为什么会有那样的输出信息。

几个注意事项

  1. 首先,为了能够顺利通过编译,在Linux上,需要使用 g++而不是gcc进行编译,因为对main这个全局变量初始化时,C语言规定必须是常量,而不能是动态确定的(最后那个printf函数地址就是动态的),同时还得加上-fpermissive 编译选项。
  2. 需要关闭模块的随机加载功能。现代操作系统为了抵抗安全攻击,可执行文件的加载基地址都进行了随机化,防止被猜测,而这段代码能够正常运行的前提是可执行文件加载基址是0x00400000。不能随机化,所以需要通过编译器来关闭。
  3. 最后,根据前面的分析其实也知道了,其实程序把main数组中的数据当成了代码在执行。在现代操作系统的安全性保护下,默认情况下是拒绝执行数据所在的内存页面的,因为这些内存页面只有读写权限,而没有可执行权限,这一安全机制叫DEP/NX。所以为了正常运行,需要把这个关闭。对于g++,添加-z execstack 编译选项即可。

总结

其实这段代码的思路并非我的原创,在国外有一个国际C语言混乱代码大赛(IOCCC, The International Obfuscated C Code Contest)。这个比赛的特点就在于写最骚的代码,实现最奇葩的效果,其中就有这样的获奖案例。

后来,国内一个大牛也原创了自己的版本,参考链接:

https://blog.youkuaiyun.com/masefee/article/details/6606813

不过,这个版本仅适用于Windows平台,我在此基础之上,又改了现在这个版本,同时支持Windows和Linux平台。

这段代码本身没有任何意义,不具备实用价值,但透过代码去研究代码和程序背后执行的底层原理,了解CPU如何调用函数、传递参数,跳转,操作堆栈,这些才是这篇文章的意义所在。

给大家留个思考题,下面这行代码能正常运行起来吗,运行起来又做了什么呢?

int main[] = {0xC3};
Unity静态合批与动态合批深度解析 阅读详情

相关推荐

Unity 3D 静态对象 Batching 静态合批 动态合批

比如游戏各个模型是独立的,它们的材质以及贴图很有可能是完全一样的,但是由于一些模型数据不一样,放入Unity中会占很多DrawCall,所以Unity提供了一个属性来做静态合批,可以将它们合并在一个Mesh里。 //--设置静态合批 首先在Edit→Project Settings→Player中勾选Static Batching静态合批和Dynamic Batching动态合批复选框。 ...

songhuanfei2017的博客 2893

C语音版飞机大战

C语音版飞机大战 不多说直接上代码 #include<iostream> #include<windows.h> #include<conio.h> #include<time.h> #include<string> using namespace std; /*=============== all the structures ===============*/ typedef struct Frame { COORD position[2];

weixin_50973371的博客 1854

Unity Shader 学习17:合批渲染

合批主要是针对这三个概念进行优化减少: ① SetPass Call:一次渲染状态切换,也就是每次切换 材质/Pass 时,就会触发一次SetPass Call ② Draw Call:cpu 调用一次 gpu 绘制函数 ③ Batch:表示一组可以一起交给gpu的Draw Call 优化结果可以在 Game窗口-Stats 或 FrameDebugger 中查看。

weixin_63623077的博客 1673

一个比较淫荡的程序源码(VC)

一个比较淫荡的程序源码(VC) 一个比较淫荡的程序,编译好之后,运行,你就会.................. (VC) 复制内容到剪贴板 代码: /* code by W.Z.T */ /* Just For Fun */ #include #include #define SLEEP_TIME1 10 #define SLEEP

日志 1万+

SAP-QM 质检管理-检验批

SAP-QM质量管理-检验批

yanzibing99的博客 9918

QT在主函数main中使用connect函数时需要注意的问题

QT

吻等离子的博客 2827

Unity3D UGUI系列之合批

详细介绍了UGUI合批的规则及原理,并给出了Demo分析。同时,附带介绍了Frame Debugger和Profiler UI怎么使用。

老王的博客 2万+

c++做的搞人代码(病毒)

c++做的整人代码

阿玥的小博客 2471

Unity中的静态合批、动态合批、GPU Instance 以及SRP Batching

Unity中的静态合批、动态合批、GPU Instance 以及SRP Batching

qq_41841073的博客 1万+

从零开始:用摩尔线程S4000运行开源AI项目的完整流程

本文详细介绍了如何使用摩尔线程S4000 GPU从零开始运行开源AI项目的完整流程,包括硬件选择、驱动安装、容器化环境配置以及典型AI项目的实战部署。通过性能优化技巧,显著提升推理速度,适合开发者快速上手国产GPU的AI应用开发。

weixin_29231725的博客 581

风控要略-互联网业务反欺诈之路(一)

当前中国互联网安全产业答题可以分为基础安全和业务安全两个领域。从互联网诞生至2014年,互联网安全行业关注的热点基本都聚焦在网络安全、系统安全和应用安全这个三大基础安全领域上,“DDOS“(分布式拒绝服务攻击)1、“漏洞“、“拖库“2和“挂马“3等大家耳熟能详的术语也是从这些领域中衍生出来的。2014年前后,随着互联网业务的爆炸式发展,黑产团伙开始从“攻击渗透系统“的传统套路进化到“利用业务风控确实进行大规模牟利“的模式,并且逐渐形成规模庞大、分工明确的黑色产业链。

Despacito1006的博客 1012

写这么骚的代码,不怕被揍么?

曾经,我接手了一份大佬的代码,里面充满了各种“骚操作”,还不加注释那种,短短几行的函数花了很久才弄懂。这世上,“只有魔法才能对抗魔法”,于是后来,翻阅各种“黑魔法”的秘籍,总结了一些比较实用的“骚操作”,让我们装X的同时,提升代码运行的效率(请配合健身房一起使用)。

IT界那些事儿 3万+

Qt5+OSG实战:如何用osgQOpenGLWidget在窗口中加载3D模型(附完整代码)

本文详细解析了如何在Qt5中使用osgQOpenGLWidget加载和渲染3D模型,涵盖环境配置、核心架构、性能优化及高级技巧。通过实战案例和完整代码,帮助开发者实现工业级3D模型渲染,提升Qt与OSG的整合效率。

weixin_42741930的博客 263

代码之外:程序员那些令人拍案叫绝的“骚操作“与技术哲学(续)

骚操作不单是技术活儿,更是脑洞与哲学的撞击火花。本文脱离枯燥代码,从AI智能、工程哲学和团队小九九出发,帮你拆解骚操作的最强配方。内容融合实操攻略和深度思考,附赠流程图、表格,让你轻松玩转骚操作,不走寻常路。

领码SPARK - 以无代码之星火,燎原数字之未来! 1579

GhidrAssist:AI驱动的逆向工程助手实战指南

逆向工程是分析软件内部工作原理、理解二进制代码逻辑的关键技术,广泛应用于软件安全、恶意代码分析和漏洞挖掘等领域。其核心原理在于通过反编译、控制流与数据流分析等手段,将机器码还原为可理解的高级语言表示。传统逆向分析高度依赖人工经验,面对海量代码时效率低下且易出错。随着大语言模型(LLM)技术的发展,其强大的代码理解与推理能力为自动化逆向分析提供了新的可能。通过将LLM与成熟的逆向工程框架(如Ghidra)相结合,可以构建智能辅助系统,实现代码意图的自动解释、变量/函数的智能重命名以及潜在漏洞模式的辅助识别,从

weixin_42547335的博客 596

YOLOV5 自动刷图脚本实战(十五)之如何找到正确的门

DXF是一个2D游戏,我们所看到的人物移动,其实是后面的背景在移动,我们所看到的画面,不过是一个完整背景中的一个镜头视角而已想要正确找到门的坐标,我们需要对不同的地图中的每个房间做一些配置信息。

qq_38892949的博客 2811

Unity中的静态合批,动态合批,SRPBatcher,GpuInstance

动态合批的条件:①使用相同的材质球②在视野范围内的物体动态合批的适用范围: 移动的,没有进行前几种合批的物体动态合批的过程:①。

Sevol_Y 3756

DSP篇--C6678功能调试系列之SPI调试

SPI传输数据分为线上直接收发和通过EDMA的数据传输两种。第一种是直接将数据放在线上(SPIDAT1),在寄存器(SPIBUF)中写入命令实现数据的收发;第二种需要配置EDMA,将数据buffer直接作为EDMA的数据源进行传输,其需要配置的参数和中断与EDMA一致。

nanke_yh的博客 1259

电脑课装b专用代码_这么骚的js代码,不怕被揍么

来自:掘金,作者:布拉德特皮链接:https://juejin.im/post/5e044eb5f265da33b50748c8前言曾经,我接手了一份大佬的代码,里面充满了各种“骚操作”,还不加注释那种,短短几行的函数花了很久才弄懂。这世上,“只有魔法才能对抗魔法”,于是后来,翻阅各种“黑魔法”的秘籍,总结了一些比较实用的“骚操作”,让我们装X的同时,提升代码运行的效率(请配合健身房一起...

weixin_39759060的博客 1万+

MTK(基础篇)ROM烧录以及分区的作用

本文介绍了MTK芯片设备的刷机流程及关键分区功能。首先通过MTK烧录工具加载ROM文件(包括引导文件和分区表),重点解析了各分区的核心作用:preloader类似BIOS负责硬件初始化;lk作为二级引导程序加载内核;vbmeta验证系统完整性;boot包含Linux内核;dtbo调整硬件配置;modem管理通信功能;tee保护生物识别数据;system/vendor分别存放系统核心和硬件驱动。特别指出preloader、vbmeta等关键分区损坏会导致设备变砖,而recovery分区是重要的救砖入口。全文简

weixin_49303682的博客 444

ESD防护设计宝典(十):RE(辐射发射)超标问题排查与解决

RE测试是EMC认证的“头号杀手”。当屏幕上出现一根根超标频谱线时,如何快速定位元凶并有效整改?本讲将带你化身电磁侦探,完成一次完美的RE问题“缉凶”。

m0_56451176的博客 394

unity3d 动态合批设置_Unity合批原理及失败的原因

1.1什么是BatchRendering问题:渲染时性能的瓶颈主要在提交渲染数据(Mesh),每次提交Mesh数据时还需要对描画State的状态进行一次设置,设置每次都会消耗大量的CPU时间.对策:减少提交次数。把具有相同State状态的数据合并到一起,一次性提交描画的Mesh的数据给GPU1.1.1 合批的类型StaticBatch 静态合批DynamicBatch 动态合批Instancing...

weixin_39842237的博客 1336

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

跑批为什么这么难

跑批数据库通常是关系型的,编写存储过程代码完成跑批计算。跑批任务涉及的数据量非常大,很可能用到所有的历史数据,而且计算逻辑复杂、步骤众多,所以跑批时间经常是以小时计的,一个任务两三小时是家常便饭,跑到十个小时也不足为奇。跑批任务采用关系数据库的SQL语句实现,运行总时间7.8小时,占用了过多的跑批时间,甚至影响了其他的跑批任务,必须优化。主要原因是跑批计算的逻辑相当复杂,即使是用传统数据库的存储过程,也常常要写几千甚至上万行代码,而分布式数据库的存储过程计算能力还比较弱,很难实现这么复杂的跑批计算。

陈橘又青的博客 3742

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

如何进行千万级别数据跑批优化?

跑批通常指代的是我们应用程序在固定日期针对某一批大量数据定时进行特定的处理,在金融业务中一般跑批的场景有分户日结、账务计提、账单逾期、不良资产处理等等,它具有高连贯性特点,通常我们执行完跑批后还要对跑批数据进行进一步处理,比如发 MQ 给下游消费,数仓拉取分析等。。。跑批最怕的就是上来就干,从不考虑涉及到第三方接口时的响应时间、大事务等问题。

Y1zH1yuan的博客 1756

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Java开源专业计算引擎:跑批真的这么难吗?

业务系统产生的明细数据通常要经过加工处理,按照一定逻辑计算成需要的结果,用以支持企业的经营活动。这类数据加工任务一般会有很多个,需要批量完成计算,在银行和保险行业常常被称为跑批,其它像石油、电力等行业也经常会有跑批的需求。大部分业务统计都会要求以某日作为截止点,而且为了不影响生产系统的运行,跑批任务一般会在夜间进行,这时候才能将生产系统当天产生的新明细数据导出来,送到专门的数据库或数据仓库完成跑批计算。第二天早上,跑批结果就可以提供给业务人员使用了。

java李阳勇的博客 9633

testmeshpro合批_Unity合批原理及失败的原因

1.1什么是BatchRendering问题:渲染时性能的瓶颈主要在提交渲染数据(Mesh),每次提交Mesh数据时还需要对描画State的状态进行一次设置,设置每次都会消耗大量的CPU时间.对策:减少提交次数。把具有相同State状态的数据合并到一起,一次性提交描画的Mesh的数据给GPU1.1.1 合批的类型StaticBatch 静态合批DynamicBatch 动态合批Instancing...

weixin_34221154的博客 1475
上一篇: 这几招技术,病毒木马经常用!
下一篇: 黑客爱用的HOOK技术大揭秘!
AI小模型
博客等级 码龄6年 6863粉丝 487原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI小模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值