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. C++中用ODBC和ADO方式连接SQL数据库

    https://wenku.baidu.com/view/f01e4e762f3f5727a5e9856a561252d380eb2033.html

  2. 洛谷 P1266 速度限制 题解

    题面 这道题可以理解为是一个分层图,也可以理解为是二维的SPFA dis[i][j]表示到达i这个点速度为j的最短路 然后跑已经死了的SPFA就好了: #include <bits/stdc++ ...

  3. layui时间显示

    第一次使用layui框架吧!记录layui踩到的坑! 个人洁癖,不太喜欢显示时分秒. 最后终于找到解决方案了! 代码如下: {field: 'ctime',title: '时间',templet:&q ...

  4. 链表分割——牛客剑指offer

    题目描述: 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 给定一个链表的头指针 ListNode pHead,请返回重新排列后的链表的头指针.注意:分割以后 ...

  5. qt 静态编译配置项

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

  6. 【原创】Linux基础之logrotate

    logrotate logrotate ‐ rotates, compresses, and mails system logs logrotate is designed to ease admin ...

  7. 利用Cmake 将最新版本OBS编译成windows版本。

    准备工作: 1. VS2013 的最新更新版或者VS2015 2. QT Creater 5.7   https://www.qt.io/ 3.  CMake (cmake-gui) 4. obs 依 ...

  8. js实现浅拷贝和深拷贝

    实现浅拷贝和深拷贝 1. 浅拷贝和深拷贝的区别 简单点说,浅拷贝拷贝完后,修改拷贝的内容可能会对源内容产生影响.而深拷贝就是拷贝前后的内容相互不影响.       那为什么拷贝前后的内容会相互影响呢? ...

  9. 【JAVASCRIPT】call和apply的用法以及区别

    function add(c,d){ return this.a + this.b + c + d; } var s = {a:"鸡", b:"你"}; con ...

  10. K nearest neighbor cs229

    vectorized code 带来的好处. import numpy as np from sklearn.datasets import fetch_mldata import time impo ...