如何在内存中执行二进制代码之win平台

2023-10-30

大家可能会很好奇,我们的任意exe程序,不就是在内存中执行的二进制机器码吗?

不,今天我要说的是,我们如何把实现指定功能的一段二进制机器码,放到我们的程序中,然后在需要的时候,直接调用它。

当然,这段代码也有其他用途,故而有了shell code的昵称,参考百度百科:

https://baike.baidu.com/item/shellcode/4051847?fr=aladdin

思考:
我们需要解决以下问题

  • 二进制代码从哪里来?
  • c/c++中如何调用它?

这些问题,接下来,都会得到解决。
不过,我们先来看看效果。

一、执行二进制代码效果

VS2017环境,x86模式下编译执行,如下代码:

#include <iostream>
#include <windows.h>

typedef int(*AddFunc)(int, int);

int main()
{
	// int add(int a, int b)函数二进制码
	unsigned char add_binaryCode_x86[] = { 0x55, 0x8b, 0xec, 0x8b, 0x45, 0x08, 0x03, 0x45, 0x0c, 0x5d, 0xc3 };

	// 申请EXECUTE属性内存
	void* execBuf = VirtualAlloc(0, sizeof(add_binaryCode_x86), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	// 拷贝二进制码
	memcpy(execBuf, add_binaryCode_x86, sizeof(add_binaryCode_x86));

	// 执行二进制码
	AddFunc func = (AddFunc)execBuf;
	int ret = func(1, 2);
	std::cout << "result:" << ret;

	// 释放内存
	VirtualFree(execBuf, 0, MEM_RELEASE);
	return 0;
}

add_binaryCode_x86中装的是add函数二进制码,C函数形式如下:

int add(int a, int b)
{
	return a + b;
}

我们不能在数组中执行二进制码,这样会报异常,故需要申请带可执行属性的内存,然后拷贝到其中,强转类型后,调用此函数。

运行结果:
在这里插入图片描述

运行结果正确,且没有报异常。

小结:

我们可以把逻辑代码封装为函数,并转换为二进制码,然后在c/c++中进行调用执行。

到此我们解决了,在c/c++中如何执行二进制码的问题。

接下来,我们来解决二进制码如何生成的问题。

二、二进制代码的生成

个人认为有2种方式生成二进制码

1.纯手写十六进制机器码

2.采用c/c++等高级语言编写程序,编译后,对其反汇编进而获得十六进制机器码

第一种,好比回到了纸带打孔编程的石器时代,需要了解x86指令集及其对应机器码,能力有限,直接放弃。

接下来,采用第二种方式,大概讲下,怎么通过自己写的函数,去提取生成的二进制码。

1. 编写一个测试程序

#include <iostream>

int add(int a, int b)
{
	return a + b;
}

int main()
{
	int ret = add(1 ,8);
	std::cout << "result:" << ret;
	return 0;
}

2. 进入Release模式调试,对它进行反汇编

注意:编译前对VS做如下配置

1、使用Release模式。近来编译器的Debug模式可能产生逆序的函数,并且会插入许多与位置相关的调用。

2、禁用优化。编译器会默认优化那些没有使用的函数,而那可能正是我们所需要的。

3、禁用栈缓冲区安全检查(/Gs)。在函数头尾所调用的栈检查函数,存在于二进制文件的某个特定位置,导致输出的函数不能重定位,这对shellcode是无意义的

设置步骤:

禁用优化:项目属性->配置属性->C/C+±>优化->优化,选择"已禁用 (/Od)"。
禁用栈缓冲区安全检查:项目属性->配置属性->C/C+±>所有选项->安全检查,选择"禁用安全检查 (/GS-)"。

开始调试,代码断在调用add()处

在这里插入图片描述

VS中,调试->窗口->反汇编,打开反汇编窗口。即可看到代码的反汇编结果:

在这里插入图片描述

图中call add (0B41080h)指令,表示调用add函数,0B41080h为add函数地址。另外前面2个push将实参1,8进行压栈。

我们继续F11,一步一步,进入add函数,如下:

在这里插入图片描述

可以看到00B41080为add函数二进制码开始地址;00B4108A为add函数二进制码结束地址,后面的2句汇编pop ebp和ret表示计算结果出栈和返回,虽然不在{}内,但是依然属于add函数的二进制码。

3. 提取add函数二进制码

VS中,调试->窗口->内存->内存1,打开内存窗口。并输入add函数二进制码开始地址00B41080,回车跳转到此地址,如下:

在这里插入图片描述

结合add函数结束地址00B4108A,得出00B41080至00B4108A=11个字节,如下:

在这里插入图片描述

故,我们把这11个字节拷贝出来,放到数组中

unsigned char xx[] = { 0x55, 0x8b, 0xec, 0x8b, 0x45, 0x08, 0x03, 0x45, 0x0c, 0x5d, 0xc3 };

这11个字节就是我们add函数编译后生成的二进制码,即最终在内存中,cpu执行的机器码。我们让这个数组按照第一节中的方式,就可以在c/c++代码中进行调用执行了。

三、从文件中读取二进制码并执行

似乎本节的意义并不大,无非就是读取出来,放到buffer中,再执行罢了,主要的执行原理、二进制码生成都已经讲完了,这个就留给大家自行扩展吧。

四、总结与思考

上述步骤讲解的是,x86 32位程序二进制码的提取,那么x64 64位下程序二进制码,如何提取,原理与此类似,依葫芦画瓢,就不再赘述了。add函数x64二进制码如下:

unsigned char add_binaryCode_x64[] = 
	{ 0x89, 0x54, 0x24, 0x10, 0x89, 0x4c, 0x24, 0x08, 0x8b, 0x44, 0x24, 0x10, 0x8b, 0x4c, 0x24, 0x08, 0x03, 0xc8, 0x8b, 0xc1, 0xc3 };

参考链接:

《Windows Shellcode学习笔记——通过VisualStudio生成shellcode》

《c执行机器码》



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:08ExecBinaryFromBuffer_win,即可下载。

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在内存中执行二进制代码之win平台 的相关文章

  • IUnknown—COM和MFC

    转自 http hi baidu com zhangqiuxi blog item 6d9603ad9c8fe5084b36d6a0 html 问题 我用MFC编写COM程序有一段时间了 知道如何使用宏和嵌套类 以及如何在嵌套类中处理IUn
  • C++ 中的虚函数及虚函数表

    C 中的虚函数及虚函数表 一 虚函数及虚函数表的定义 二 虚函数表指针和虚函数表的创建时机 三 虚函数实现多态的原理 一 虚函数及虚函数表的定义 虚函数 虚函数就是在基类中定义一个未实现的函数名 使用虚函数的核心目的就是通过基类访问派生类定
  • 解决“17: 错误:程序中有游离的‘\240’,\302’

    参考链接 https blog csdn net asuphy article details 54602426 执行如下命令即可 sed i s o240 o302 g dy haikang test cpp
  • C++工程师复习题

    一 auto ptr 类使用必须满足下列限制 1 不要使用 auto ptr 对象保存指向静态分配对象的指针 2 不要使用两个 auto ptrs 对象指向同一对象 3 不要使用 auto ptr 对象保存指向动态分配数组的指针 4 不要将
  • C/C++中浮点数格式学习——以IEEE75432位单精度为例

    这是浮点数的通常表示形式 在IEEE754中 单精度浮点数有如下形式 32位单精度 单精度二进制小数 使用32个比特存储 1 8 23位长 S Exp Fraction 31 30至23偏正值 实际的指数大小 127 22至0位编号 从右边
  • C/C++ 引用作为函数的返回值

    语法 类型 函数名 形参列表 函数体 特别注意 1 引用作为函数的返回值时 必须在定义函数时在函数名前将 2 用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本 代码来源 RUNOOB include
  • 经典面试题之new和malloc的区别

    new和malloc的区别是C C 一道经典的面试题 我也遇到过几次 回答的都不是很好 今天特意整理了一下 0 属性 new delete是C 关键字 需要编译器支持 malloc free是库函数 需要头文件支持 1 参数 使用new操作
  • GDAL多光谱与全色图像融合简单使用

    目录 简述 C 代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 最近在GDAL的代码中看见了gdalpa
  • vector,list,deque区别

    http blog csdn net renkaihao article details 6803866 vector和built in数组类似 它拥有一段连续的内存空间 并且起始地址不变 因此它能非常好的支持随即存取 即 操作符 但由于它
  • C++中的RTTI

    文章目录 dynamic cast运算符 指针类型的dynamic cast 引用类型的dynamic cast typeid运算符 使用RTTI type info类 参考资料 RTTI Runtime Type Information
  • 【干货】Chrome插件(扩展)开发全攻略(不点进来看看你肯定后悔)<转>

    干货 Chrome插件 扩展 开发全攻略 不点进来看看你肯定后悔 写在前面 我花了将近一个多月的时间断断续续写下这篇博文 并精心写下完整demo 写博客的辛苦大家懂的 所以转载务必保留出处 本文所有涉及到的大部分代码均在这个demo里面 h
  • ATL字符串转换宏

    有比MultiByteToWideChar和WideCharToMultiByte更简单的字符串转换宏 你相信吗 头文件 d program files microsoft visual studio 8 vc atlmfc include
  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • lua和测试(一)

    lua做为一门高级语言 在游戏产业运用到机会越来越多了 测试掌握几门脚本语言也有一定的重要性 以下对于lua组合输入做出一些引导 测试需要掌握的关于返回数值 主要用到布尔类 前言的指引 lua的语法比较简单和清晰 学过c语言的可以很好的掌握
  • Trace Function Enter, Exit and Leave

    http developer nokia com community wiki Trace Function Enter Exit and Leave
  • Dev-C++之开启装逼效果

    Dev C 是个不错的C IDE 在10年前 它是很不错 在现在 它是个以界面丑陋和调试像吃粑粑这两点著称 如下图 实在是丑到离谱 丑到无法忍受 可是没办法呀 人家CCF规定比赛用这个 你个小蒟蒻吵什么 我现在就来讲讲怎么把你的Dev C
  • 在聚会中常玩数七的游戏,七的倍数和带有七的数字都不能说,比如14,27,28。请找出1~100的不能说的数字。...

    利用ES5的filter高阶函数来实现 var arr 1 2 3 4 5 6 7 17 27 21 22 28 100 r arr filter function x return x 10 7 x 7 0 alert r 7 14 17
  • Open3D(C++)实现建筑物点云立面和平面分割提取

    Open3D C 实现建筑物点云立面和平面分割提取 近年来 点云技术在城市规划 机器人地图构建等领域得到广泛应用 本篇文章将介绍如何利用Open3D C 库实现建筑物点云立面和平面分割提取 准备工作 首先需要编译安装Open3D库 本文使用
  • C/C++编程:令人印象深刻的高级技巧案例

    C C 编程语言在软件开发领域有着悠久的历史 由于其高效 灵活和底层访问能力 至今仍然被广泛应用 本文将介绍一些在C C 编程中令人印象深刻的高级技巧 帮助读者提升编程水平 更加高效地使用这两种强大的编程语言 一 指针运算与内存管理 C C
  • C 语言运算符详解

    C 语言中的运算符 运算符用于对变量和值进行操作 在下面的示例中 我们使用 运算符将两个值相加 int myNum 100 50 虽然 运算符通常用于将两个值相加 就像上面的示例一样 它还可以用于将变量和值相加 或者将变量和另一个变量相加

随机推荐

  • PHPStorm常用配置纪录

    PHPStorm 下载及主题样式下载 http www lanmps com lanmps tools html 主题 Preferences gt Appearance Behavior gt Appearance Theme 选择 Da
  • SPLUNK 简单查询语句

    Here is offical document http docs splunk com Documentation SplunkCloud 7 0 2 Search GetstartedwithSearch inputlookup al
  • 【华为OD机试】跳房子1 (C++ Python Java)2023 B卷

    题目描述 跳房子 也叫跳飞机 是一种世界性的儿童游戏 游戏参与者需要分多个回合按顺序跳到第1格直到房子的最后一格 跳房子的过程中 可以向前跳 也可以向后跳 假设房子的总格数是count 小红每回合可能连续跳的步教都放在数组steps中 请问
  • Intel DDR布线之Tabbed Routing

    一 Overview Tabbed Routing是一种在相邻的平行走线上连接小的梯形凸片 以更积极地控制走线的电容 以管理走线阻抗并补偿结构的电感效应的方法 Tabbed Routing is a method of attaching
  • Ubuntu18.04安装OpenGL依赖库

    sudo apt get install build essential sudo apt get install libgl1 mesa dev sudo apt get install libglu1 mesa dev sudo apt
  • tabpanel页签的机制

    tabpanel页签的机制 页签展现渲染时 只会初始化渲染你所指定的activeTab这个子页签 其他的页签一律不渲染 所以也就不存在form的dom内容 如果没有指定activeTab页签不会初始化任何子页签 那么所有的form都不会得到
  • 【ARIMA-WOA-CNN-LSTM】合差分自回归移动平均方法-鲸鱼优化-卷积神经网络-长短期记忆神经网络研究(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 1 1 ARIMA模型 1 2 鲸鱼优化算法 1 3 卷积神经网络 1 4 LSTM 模型 2 运行结果
  • uniapp Echart X轴Y轴文字被遮挡怎么办,或未能铺满整个容器

    有时候布局太小 使用echarts x轴y轴文字容易被遮挡 怎么解决这个问题呢 或者是未能铺满整个容器 方法1 直接设置 containLabel 字段 options grid containLabel true 方法2 间接设置 但是不
  • IDEA 【基础】 javaweb项目中 将maven的jar包,复制到web项目的 lib 文件夹

    自己在做小型javweb项目的时候经常遇到这种问题 java lang NoClassDefFoundError 明明maven已经添加依赖了 而且项目里面可以正常运行 但是启动tomcat运行的时候 却运行不了 博主琢磨半天 了解到 第三
  • 深度学习实战项目(三)-行人检测重识别yolov5+reid(跑通+界面设计)

    行人检测重识别yolov5 reid 跑通 界面设计 参考源代码 github 权重文件 根据github上面的网盘进行权重下载 检测 将 ReID resnet50 ibn a pth放在person search weights文件下
  • One-Stage Visual Grounding(单阶段语言指示的视觉定位)论文略读_2019-2020

    One Stage Visual Grounding 2019 2020年论文略读 1 Zero Shot Grounding of Objects from Natural Language Queries 2019 ICCV 改进工作
  • Linux 查看显卡型号

    输入以下命令 lspci grep i vga 可以查看显卡型号 但是是一串数字代码 可通过PCI devices网站进行查询 结果如下所示 GeForce RTX 3060 Lite Hash Rate 即为显卡信息
  • 浏览器刷新、关闭页面与统计在线人数

    项目中可能需要统计在线人数 也可能需要在用户在退出时进行用户注销登录 既为统计实时在线人数 也为及时清理暂时不再使用的session 节约资源提高性能 对于以上的情况 若用户使用页面的注销按钮退出登录 那一定万事大吉了 当实际中这种可能性很
  • Java面试题(1)-J2SE基础

    最近在为自己实习准备 看了网上各种面试经验贴 也和身边的小伙伴一起参加了不少牛逼IT企业的面试 这篇文章就将面试遇到的一些比较常见的问题整理一下 给大家一些参考 也为自己整理整理 J2SE基础 1 九种基本数据类型的大小 以及他们的封装类
  • 猿创征文

    猿创征文 国产数据库实战 使用docker部署PolarDB X云原生分布式开源数据库 一 PolarDB X介绍 1 PolarDB X简介 2 PolarDB X特点 二 检查docker版本 三 检查docker配置信息 四 下载Po
  • redis集群原理

    redis是单线程 但是一般的作为缓存使用的话 redis足够了 因为它的读写速度太快了 官方的一个简单测试 测试完成了50个并发执行100000个请求 设置和获取的值是一个256字节字符串 结果 读的速度是110000次 s 写的速度是8
  • MySQL高频面试题

    文章目录 1 什么是MySQL 2 关系型数据库和非关系型数据库 3 数据库三大范式是什么 4 一条 SQL 查询语句是如何执行的 5 引擎 MySQL存储引擎MyISAM与InnoDB区别 MyISAM索引与InnoDB索引的区别 Inn
  • 哈夫曼树带权路径长度

    一 长什么样 左边是普通树 右边是哈夫曼树 图a WPL 5 2 7 2 2 2 13 2 54 图b WPL 5 3 2 3 7 2 13 1 48 可见 图b的带权路径长度较小 我们可以证明图b就是哈夫曼树 也称为最优二叉树 二 怎么生
  • Vue实现swiper轮播组件

    目前市面上有很多轮播组件 但是有的不满足业务需求 因此也需要自己首先轮播组件 以下是一个用vue实现的轮播组件 带动画效果 可以自行设置轮播速度 选择是否需要分页器等 效果如下 思路 结构 一个轮播组件应该由三部分组成 一是轮播的元素 如图
  • 如何在内存中执行二进制代码之win平台

    大家可能会很好奇 我们的任意exe程序 不就是在内存中执行的二进制机器码吗 不 今天我要说的是 我们如何把实现指定功能的一段二进制机器码 放到我们的程序中 然后在需要的时候 直接调用它 当然 这段代码也有其他用途 故而有了shell cod