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. qt 静态编译配置项

    configure -confirm-license -opensource -platform win32-msvc2013 -debug-and-release -static -prefix & ...

  2. bat 将war文件转换成ear文件

    1.无需拷贝war文件,自动获取war set path=%path%;D:\jdk\jdk1.6.0_31\bin;C:\Program Files\7-Zip del **0001-control ...

  3. 【Opencv 源码剖析】 一、 create函数

    1. inline Mat::Mat(int _rows, int _cols, int _type) : size(&rows) { initEmpty();//将data.cols.row ...

  4. 向PHP发送HTTP-Get请求

    1.get.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  5. js定时器 离开当前页面任然执行的问题

    今天在博客上看到有人问 js定时器-----离开当前页面原本匀速运动的div加速了,回到页面若干时间恢复匀速??? 他是js定时器控制一个盒子做旋转动画 离开页面后js还在执行 但是盒子这个dom却被 ...

  6. .NET中跨线程访问winform控件的方法

    1 第一种方式 MethodInvoker invoker = () => { richTextBox1.AppendText(_ClientSocketModelConnectedEvent. ...

  7. Docker 容器数据卷(Data Volume)与数据管理

    卷(Volume)是容器中的一个数据挂载点,卷可以绕过联合文件系统,从而为Docker 提供持久数据,所提供的数据还可以在宿主机-容器或多个容器之间共享.通过卷,我们可以可以使修改数据直接生效,而不必 ...

  8. DNS缓存失败怎么解决?

    DNS的中文名是域名系统,是域名和IP地址相互映射的一个分布式数据库.有了DNS,我们上网时直接输入网站域名(即网址)即可,而不用输入网站的IP地址访问网站,对于用户来说比较方便记忆和访问. 每次当我 ...

  9. 【异常】lockfile.AlreadyLocked: ~/airflow/airflow-scheduler.pid is already locked

    1 完整异常信息 File "/usr/bin/airflow", line 32, in <module> args.func(args) File "/u ...

  10. java-集合学习-底层实现

    集合分为两大类: Collection集合: 单个存储 Map集合: 按<键,值>对的形式存储,  <员工姓名,工资> Collection类关系图 Collection常见方 ...