1.裸函数
c语言的普通函数中即使什么都不写,编译器在编译时也会给它加上一些汇编代码;比如开栈、返回等;
裸函数就是编译器什么都不管,一切都需要自己来处理;
裸函数的优点是自由度高,可以应用在钩子程序等地方;
 
例如:一个裸函数
#include "stdio.h"
 
void __declspec(naked) method(){
 
 
}
 
int main(int argc, char* argv[])
{
    
    method();
    return 0;
}
 
空的裸函数由于编译器不会给它添加返回指令,所以运行时会报错,可以自己在其中添加汇编指令;
void __declspec(naked) method(){
    __asm{
        ret
    }
}
然后反汇编时就有了ret指令:
 
1)裸函数实现加法运算
#include "stdio.h"
 
//实现1+2+3+4+5+6
int __declspec(naked) method(int x, int y, int z){
    __asm{
        //保留之前的栈底
        push ebp
        //开栈        ->注意十六进制数前最好加上0x防止被当成十进制数
        mov ebp,esp
        sub esp,0x40
        //保护现场
        push ebx
        push esi
        push edi
        //填充缓冲区
        lea edi,[ebp-0x40]
        mov ecx,0x10
        mov eax,0xcccccccc
        rep stos dword ptr [edi]
        //往缓冲区存放局部变量
        mov dword ptr [ebp-0x04],4
        mov dword ptr [ebp-0x08],5
        mov dword ptr [ebp-0x0c],6
        //计算
        mov eax,dword ptr [ebp+0x08]
        add eax,dword ptr [ebp+0x0c]
        add eax,dword ptr [ebp+0x10]
        add eax,dword ptr [ebp-0x04]
        add eax,dword ptr [ebp-0x08]
        add eax,dword ptr [ebp-0x0c]
        //恢复现场
        pop edi
        pop esi
        pop ebx
        //恢复堆栈
        mov esp,ebp
        pop ebp
        //返回
        ret
    }
}
 
//同效果的函数
int plus(int x, int y, int z){
    int a = 4;
    int b = 5;
    int c = 6;
    return x+y+z+a+b+c;
}
 
int main(int argc, char* argv[])
{
    
    method(1,2,3);
    //plus(1,2,3);
 
    return 0;
}
 
2.调用约定
常用的调用约定:
调用约定
参数压栈顺序
平衡堆栈
__cdecl
从右至左入栈
调用者清理栈
__stdcall
从右至左入栈
自身清理堆栈
__fastcall
ECX/EDX传送前两个
剩下:从右至左入栈
自身清理堆栈
1)外平栈
默认调用约定;
int __cdecl Plus(int a, int b)                
{                
    return a+b;            
}                
                
push        2                
push        1                
call        @ILT+15(Plus) (00401014)                
add         esp,8
 
2)内平栈
ret后面的参数为平衡堆栈所需的内存单元数;
int __stdcall Plus(int a, int b)            
{            
    return a+b;        
}            
            
push        2            
push        1            
call        @ILT+10(Plus) (0040100f)            
            
函数内部:            
ret         8    
 
3)快速调用
用寄存器来保存参数;
最多两个参数可保存在寄存器;
不需要平衡堆栈;
比较适合用在参数多次传递的地方,比如循环;
 
int __fastcall Plus(int a, int b)                
{                
    return a+b;            
}                
                
mov         edx,2                
mov         ecx,1                
call        @ILT+0(Plus) (00401005)                
                
函数内部:                
ret                         
 
4)超过两个参数的快速调用
其它参数保持在栈中,用内平栈的方式来平衡堆栈;
int __fastcall Plus4(int a, int b,int c,int d)                    
{                    
    return a+b+c+d;                
}                    
                    
push        4                    
push        3                    
mov         edx,2                    
mov         ecx,1                    
call        @ILT+5(Plus) (0040100a)                    
                    
函数内部:                    
                    
ret         8    
 
3.函数入口
(1) main 或WinMain 是“语法规定的用户入口”,而不是“应用程序入口”。应用程序入口通常是启动函数。
例如:vc6中修改入口函数名
右键单击项目-》Setting
 
(2) mainCRTStartup 和 wmainCRTStartup 是控制台环境下多字节编码和Unicode 编码的启动函数.    
而WinMainCRTStartup 和wWinMainCRTStartup 是windows 环境下多字节编码和Unicode 编码的启动函数.
可以在vc6的调用栈中看到:
 
3)找main函数
    main 函数被调用前要先调用的函数如下:    
    GetVersion()      ->获得当前操作系统的版本   
    _heap_init()      ->初始化堆空间的大小   
    GetCommandLineA()    ->获取命令行参数     
    _crtGetEnvironmentStringsA()     ->获取环境变量    
    _setargv()    
    _setenvp()    
    _cinit()
 
查看mainCRTStartup()可知main函数在经过编译器处理后有3个参数:
 
这些函数调用结束后就会调用main 函数,根据main 函数调用的特征,将3 个参数压入栈内作为函数的参数。
也就是说找到上面的函数后,它们后面如果有传入3个参数的函数被调用,那么这个函数很可能就是main函数;
 
4.逆向实例
1)找到程序入口main
用od打开目标程序进行分析:
 
2)进入mian函数分析
这里有个两个函数,只分析第一个;
注意:参数的入栈顺序是从右向左的;
目前可以确定的程序代码:
#include "stdio.h"
 
int __fastcall plus(int a, int b, int c, int d, int e){
 
}
 
int main(int argc, char* argv[]){
    plus(1,3,4,6,7);
    return 0;
}
 
3)分析第一个函数
可以确定的代码:
#include "stdio.h"
 
int __stdcall plus1(int x, int y, int z){
        
}
 
 
int plus2(int m, int n){
    
}
 
int __fastcall plus(int a, int b, int c, int d, int e){
    int j = plus1(a,b,c);
    int k = plus2(a,b);
    
    return plus2(j,k);
 
}
 
int main(int argc, char* argv[]){
    plus(1,3,4,6,7);
    return 0;
}
 
3)分析函数的子函数
用同样的方法分析另一个子函数;
代码:
#include "stdio.h"
 
int __stdcall plus1(int x, int y, int z){
     return x+y+z;   
}
 
int plus2(int m, int n){
    return m+n;
}
 
 
int __fastcall plus(int a, int b, int c, int d, int e){
    int j = plus1(a,b,c);
    int k = plus2(a,b);
    
    return plus2(j,k);
}
 
 
int main(int argc, char* argv[]){
    plus(1,3,4,6,7);
    return 0;
}
 
4)查看程序结果
接着运行到函数调用完,查看结果,结果保存在eax中;
换算成十进制:
0x0c=12
也就是结果为:12
(1+3+4)+(1+3)=12;
和分析的结果一样;
 
 
 
 
 

简单的c程序分析的更多相关文章

  1. 第一讲 一个简单的Qt程序分析

    本文概要:通过一个简单的Qt程序来介绍Qt程序编写的基本框架与一些Qt程序中常见的概念 #include <QApplication> #include <QPushButton&g ...

  2. UNP学习笔记2——从一个简单的ECHO程序分析TCP客户/服务器之间的通信

    1 概述 编写一个简单的ECHO(回复)程序来分析TCP客户和服务器之间的通信流程,要求如下: 客户从标准输入读入一行文本,并发送给服务器 服务器从网络输入读取这个文本,并回复给客户 客户从网络输入读 ...

  3. 通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    秦鼎涛  <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验一 通过汇编一个简单的C程序,分析汇编代码 ...

  4. 第一周:通过汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    姓名:吕松鸿 学号:20135229 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...

  5. 通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的

    实验一:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的 学号:20135114 姓名:王朝宪 注: 原创作品转载请注明出处   <Linux内核分析>MOOC课程http: ...

  6. Flink源码分析 - 剖析一个简单的Flink程序

    本篇文章首发于头条号Flink程序是如何执行的?通过源码来剖析一个简单的Flink程序,欢迎关注头条号和微信公众号"大数据技术和人工智能"(微信搜索bigdata_ai_tech) ...

  7. 对Java数组中去除重复项程序分析

    我作为一个Java菜鸟,只会用简单的办法来处理这个问题.如果有大神看到,请略过,感激不尽! 所以首先先分析这道题目:数组中重复的数据进行删除,并且要让数组里的数据按原来的顺序排列,中间不能留空. 既然 ...

  8. 编写一个简单的C++程序

    编写一个简单的C++程序 每个C++程序都包含一个或多个函数(function),其中一个必须命名为main.操作系统通过调用main来运行C++程序.下面是一个非常简单的main函数,它什么也不干, ...

  9. C#编写简单的聊天程序

    这是一篇基于Socket进行网络编程的入门文章,我对于网络编程的学习并不够深入,这篇文章是对于自己知识的一个巩固,同时希望能为初学的朋友提供一点参考.文章大体分为四个部分:程序的分析与设计.C#网络编 ...

随机推荐

  1. 使用rpm安装mysql 5.7和依赖们

    在安装mysql-server之前,需要安装相应的依赖,当前系统环境是CentOS7,需要安装3个依赖,mysql-community-common,mysql-community-libs,mysq ...

  2. C++字符转换等常用方法

    下面是C++中字符编码格式转换(包括ANSI转换为UTF8,UTF8转换为ANSI,ANSI转换为unicode,unicode转换为ANSI,UTF8转换为unicode,unicode转换为UTF ...

  3. nginx代理 tomcat获得真实用户IP

    nginx代理 tomcat获得真实用户IP 2017年04月08日 21:34:17 cf 阅读数 1825更多 分类专栏: nginx html/js/ajax   版权声明:本文为博主原创文章, ...

  4. MySQL中的主键约束和外键约束

    1.主键约束 表通常具有包含唯一标识表中每一行的值的一列或一组列. 这样的一列或多列称为表的主键 (PK),用于强制表的实体完整性. 由于主键约束可保证数据的唯一性,因此经常对标识列定义这种约束. 如 ...

  5. 啥叫K8s?啥是k8s?

    •Kubernetes介绍 1.背景介绍 云计算飞速发展 - IaaS - PaaS - SaaS Docker技术突飞猛进 - 一次构建,到处运行 - 容器的快速轻量 - 完整的生态环境 2.什么是 ...

  6. Bootstrap3基础教程 01 概述

    移动设备优先是 Bootstrap 3 的最显著的变化. 基础的页面: <!DOCTYPE html> <html> <head> <meta charset ...

  7. Myeclipse启动后tomcat空指针异常

    今天早上吃完早餐来公司上班,打开电脑,输入密码,123456.....嗯……,再打开myeclipse,duang...duang...duang....tomcat空指针异常,tmd我这暴脾气昨天还 ...

  8. element-ui中关闭对话框清空验证,清除form表单数据

    对于elementUI中对话框,点击对话框和关闭按钮 怎么清空验证,清空form表单,避免二次点击还会有 验证错误的提示.今天终于自己查资料解决了,分享给大家 1.首先在你的对话框 取消按钮 加一个c ...

  9. vue进阶:vuex(数据池)

    非父子组件传值 vuex 一.非父子组件传值 基于父子组件通信与传值实现非父子组件传值的示例关键代码: <template> <div> <!-- 学员展示 --> ...

  10. 美国cst时间和夏令时

    美国6 PM CST相当于北京时间几点? 换算北京时间是:8:00,上午8点,日期是第二天.(换算公式:18点+14小时=第二天8点) 6 PM CST:6:00 PM Central Standar ...