前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >手动编写C函数的汇编代码

手动编写C函数的汇编代码

作者头像
信安本原
发布于 2020-04-22 07:02:52
发布于 2020-04-22 07:02:52
1K00
代码可运行
举报
文章被收录于专栏:信安本原信安本原
运行总次数:0
代码可运行

在前面的文章里已经清楚计算机是只认识0和1的,那平时编写的程序到运行中间又经历了什么?

这个过程用下面一张图就足以说明所有的问题了

稍微解释一下其中的一些含义

目标文件和可执行文件都是由机器语言指令组成的 目标文件只包含你写的代码所翻译的机器语言代码 可执行文件还包含你写的代码中使用的库函数和启动代码的机器语言代码(启动代码充当着程序和操作系统之间的接口)

编译器到底生成了什么

多说无益,这里用一个空白的C语言函数来看看编译器生成了哪些东西。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void test() {00ED1E70  push        ebp  00ED1E71  mov         ebp,esp  00ED1E73  sub         esp,0C0h  00ED1E79  push        ebx  00ED1E7A  push        esi  00ED1E7B  push        edi  00ED1E7C  lea         edi,[ebp-0C0h]  00ED1E82  mov         ecx,30h  00ED1E87  mov         eax,0CCCCCCCCh  00ED1E8C  rep stos    dword ptr es:[edi]  00ED1E8E  mov         ecx,offset _0BADB893_Source@cpp (0EDC000h)  00ED1E93  call        @__CheckForDebuggerJustMyCode@4 (0ED1208h)  
}00ED1E98  pop         edi  00ED1E99  pop         esi  
}00ED1E9A  pop         ebx  00ED1E9B  add         esp,0C0h  00ED1EA1  cmp         ebp,esp  00ED1EA3  call        __RTC_CheckEsp (0ED1212h)  00ED1EA8  mov         esp,ebp  00ED1EAA  pop         ebp  00ED1EAB  ret

中间的检查堆栈平衡等函数我们可以省略,仔细看看其中的汇编代码,很容易可以看出这其中所进行的操作就是上一篇文章所画的堆栈图,堆栈图也是后面进行分析的关键,手写这段程序的代码也是一键很重要的事情,如果所有的操作都交给编译器去做,那你所有的操作就都是很明确的,又怎么去跟别人进行对抗。

手动编写

这里就需要引入裸函数的概念了,裸函数就是编译器不帮你生成一行代码,所有的代码都必须你自己去手动编写

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void __declspec(naked) Function(){
}

在正常情况下,我们写一个空函数是不会出现报错的情况的,但是裸函数则不然,直接用上面的方式写,会跳到一个程序不认识的地方,如果对上一篇文章的堆栈图足够了解,就会知道造成这个情况的原因是什么。

这是因为函数在汇编语言中是通过call来调用的,这个操作包含了两个步骤,一步是把下一条指令的地址push到堆栈中,一步是跳转到函数所要执行的地址,如果是一个空函数,它会再跳回到call指令的下一条地址,但是裸函数不会,因为编译器没有给我们生成任何一条指令,所以要想让一个空的裸函数正常运行, 就需要我们手动添加一段指令,让程序回到原来要执行的位置,那就是添加ret指令,所以可以运行的空的裸函数如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void __declspec(naked) Function(){    __asm    {        ret    }}

对于手动编写要特别注意对于相关数据的调用,需要明确它们所处的位置在哪里,为了把所有的情况都包含在内,用了下面的这个例子作为说明

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int plus(int x, int y, int z) {    int a = 1;    int b = 2;    int c = 3;    return x + y + z + a + b + c;}

其中x、y、z和a、b、c在内存中所存在的位置是完全不同的,想要分清楚这个内容,上一篇文章的堆栈图就特别的关键了,不清楚的去看上一篇文章的说明。

下面直接给出最终的代码,跟编译器所生成的肯定是有差别的,但是在功能实现方面已经足够了,想要看懂其中的含义,堆栈图是必须的,堆栈图是必须的,堆栈图是必须的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int plus(int x, int y, int z) {    int a = 1;    int b = 2;    int c = 3;    return x + y + z + a + b + c;}
int __declspec(naked) plus1(int x, int y, int z) {    __asm {        //保存栈底        push ebp
        //提升堆栈        mov ebp,esp        sub esp,0x40
        //保护现场        push ebx        push esi        push edi
        //填充缓冲区        mov eax,0xCCCCCCCC        mov ecx,0x10        lea edi,dword ptr ds:[ebp-0x40]        rep stosd
        //函数功能        mov dword ptr ds:[ebp-0x8],1        mov dword ptr ds:[ebp-0xC],2        mov dword ptr ds:[ebp-0x10],3
        mov eax,dword ptr ds:[ebp+0x8]        add eax,dword ptr ds:[ebp+0xC]        add eax,dword ptr ds:[ebp+0x10]        add eax,dword ptr ds:[ebp-0x8]        add eax,dword ptr ds:[ebp-0xC]        add eax,dword ptr ds:[ebp-0x10]
        //恢复现场        push edi        push esi        push ebx
        //恢复堆栈        mov esp,ebp        pop ebp
        //返回        ret    }}void test() {
}
int main(int argc, char* argv[]) {    //test();    //plus(1, 2, 3);    plus1(1, 2, 3);    return 0;}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 无心的梦呓 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
大神洗礼第四讲——函数相关及编程技巧
Author:bakari       Date:2012.11.2 1、参数传递问题: < 1 >、堆栈传参 < 2 >、寄存器传参(利用通用寄存器进行函数参数传递的方法) < 3 >、全局变量或静态变量传参 2、 Call Convention(函数调用约定) < 1 >、_cdecl a、 参数从右向左压入堆栈 b、 函数被调用者修改堆栈 c、 在win32应用程序里,宏APIENTRY,WINAPI,都表示_stdcall,非常常见. d、 C和C++程序的缺省调用方式。每一个调用它的函数都包含清空
Linux云计算网络
2018/01/10
6660
大神洗礼第四讲——函数相关及编程技巧
红队 | InlineHook技术实现
IATHOOK局限性较大,当我们想HOOK一个普通函数,并不是API,或者IAT表里并没有这个API函数(有可能他自己LoadLibrary,自己加载的),那我们根本就从导入表中找不到这个函数,自然也就在IAT表中无法找到,InlineHook算是对IATHOOK一个升级版吧
HACK学习
2021/05/14
6250
【C语言】汇编角度剖析函数调用的整个过程
压栈操作,他会改变esp所指向的位置,从而适应栈帧空间的扩大,操作方式就是将操作数直接压栈到栈帧空间
举杯邀明月
2023/04/12
1.5K0
【C语言】汇编角度剖析函数调用的整个过程
C/C++ 反汇编:函数与结构体
反汇编即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编对软件调试、系统漏洞挖掘、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面。
微软技术分享
2022/12/28
1.2K0
C/C++ 反汇编:函数与结构体
你一定要搞明白的C函数调用方式与栈原理
这绝对不是标题党。而是C/C++开发中你必须要掌握的基础知识,也是高级技术岗位面试中高频题。我真的真的真的希望无论是学生还是广大C/C++开发者,都该掌握此文中介绍的知识。
范蠡
2018/07/25
3.4K0
你一定要搞明白的C函数调用方式与栈原理
C/C++ 反汇编:多维数组与指针
反汇编即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编对软件调试、系统漏洞挖掘、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面。
微软技术分享
2022/12/28
7380
C/C++ 反汇编:多维数组与指针
C/C++ 反汇编:数据类型与常量
反汇编即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编对软件调试、系统漏洞挖掘、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面。
微软技术分享
2022/12/28
4140
C/C++ 反汇编:数据类型与常量
C/C++ 反汇编:分析类的实现原理
反汇编即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编对软件调试、系统漏洞挖掘、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面。下面将分析VS 2013 编译器产生C代码的格式与实现方法,研究一下编译器的编译特性。
微软技术分享
2022/12/28
6030
C/C++ 反汇编:分析类的实现原理
函数栈帧的创建与销毁
最近在学习C语言的过程中遇到了一些问题,在询问老师和查询相关资料的基础上了解到了函数栈帧的相关概念,对下列问题也有了答案。
摘星
2023/04/28
5350
函数栈帧的创建与销毁
菜鸟 学注册机编写之 “MD5”
sc_office_2003_pro 高手不要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜感激!   
我是小三
2018/08/08
5060
菜鸟  学注册机编写之 “MD5”
【C语言】函数——栈帧的创建和销毁
✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 ---- 目录 前言😄 什么是栈🔑 什么是函数的栈帧🔑 认识相关寄存器和汇编指令🔑 寄存器🔥 相关的汇编指令:🔥 函数的调用堆栈🔑 函数栈帧的创建🔑 分析栈帧的创建:💧 为什么会出现“烫烫烫”:💧 分析main函数中的核心代码:💧 分析Add函数的传参💧 函数调用过程💧 函数栈帧的销毁下🔑 结语✍ ---- 前言😄 好的,各位,我们前面就已经学过函数的一些相关知识了
平凡的人1
2022/11/15
6260
【C语言】函数——栈帧的创建和销毁
函数调用时堆栈的变化情况
函数的正常运行必然要利用堆栈,至少,函数的返回地址是保存在堆栈上的。函数一般要利用参数,而且内部也会用到局部变量,在对表达式进行求值时,编译器还会生成一些无名临时对象,这些对象都是存放在堆栈上的。
恋喵大鲤鱼
2018/08/03
7660
函数调用时堆栈的变化情况
构建API调用框架绕过杀软hook
我们知道杀软在API函数的监控上一般有两种手段,一种是在3环直接通过挂钩到自己的函数判断是否调用了这个API,另外一种方式就是在0环去往SSDT表的路径上挂钩来判断进0环后的操作。那么我们如果不想杀软监控我们的行为,之前提过的内核重载是一种绕过的方式,但是内核重载的动静太大,这里我们就通过直接重写3环到0环的API,通过重写KiFastCallEntry来自己调用内核的函数,以达到规避杀软的效果。
红队蓝军
2022/04/26
1.1K1
构建API调用框架绕过杀软hook
滴水逆向初级-C语言(二)
1、声明变量 变量类型变量名; 变量类型用来说明宽度是多大 int 4个字节 short 2个字节 char 1个字节
zhang_derek
2021/04/13
1.3K0
C/C++ 反汇编:流程控制与循环结构
反汇编即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编对软件调试、系统漏洞挖掘、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面。
微软技术分享
2022/12/28
3350
C/C++ 反汇编:流程控制与循环结构
C语言函数的栈帧详解
一个限定表尾进行删除(出栈)和插入(入栈)操作的线性表,其过程类似与压子弹与退子弹(后进先出)。 一个由系统自动分配的内存空间,譬如调用函数、创建临时变量时内存空间的创建与销毁。 用于存储函数内部的局部变量、方法调用、函数传参数值等。 由高地址向低地址生长。
CtrlX
2022/10/27
2.2K0
C语言函数的栈帧详解
C语言——F/函数的栈帧的创建和销毁
函数栈帧(stack frame)就是函数调用过程中在程序的调用栈(call stack)所开辟的空间,这些空间是用来存放:
用户11015888
2024/03/11
1310
C语言——F/函数的栈帧的创建和销毁
[C语言]函数栈帧的创建和销毁
返回值并不会随着函数作用域的销毁而销毁,而是放在eax中准备返回,当通过pop出栈回到main函数中再将返回值放到局部变量中。
IT编程爱好者
2023/04/12
5510
[C语言]函数栈帧的创建和销毁
汇编角度看函数堆栈调用
带着以下一个问题来探索: (1)形参的内存空间的开辟和清理是由调用方还是由被调用方执行的? (2)主函数调用函数结束后,主函数从哪里开始执行?从头开始还是从调用之后开始? (3)返回值是如何带出来的?
lexingsen
2022/02/24
6750
汇编角度看函数堆栈调用
5.5 汇编语言:函数调用约定
函数是任何一门高级语言中必须要存在的,使用函数式编程可以让程序可读性更高,充分发挥了模块化设计思想的精髓,今天我将带大家一起来探索函数的实现机理,探索编译器到底是如何对函数这个关键字进行实现的,并使用汇编语言模拟实现函数编程中的参数传递调用规范等。
微软技术分享
2023/08/22
3370
相关推荐
大神洗礼第四讲——函数相关及编程技巧
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文