zoukankan      html  css  js  c++  java
  • 逆向分析技术

    1 . 函数参数传递

    调用约定 _cdecl _stdcall  _fastcall
    传参顺序 左->右 右->左 寄存器和堆栈传参
    平衡堆栈者 调用者 子程序 子程序
    int _cdecl Add(char a, long b, int c, int d)
    
    {
    return (a + b + c + d);
    }
    

    push 4
    push 3
    push 2
    push 1
    call Add (1161A60h)
    add esp,10h //调用者平衡堆栈

    int _stdcall Add(char a, long b, int c, int d)
    {
        return (a + b + c + d);
    }

    push 4 
    push 3 
    push 2 
    push 1 
    call Add (1161A60h) //子程序Add里面平衡堆栈

    int _fastcall Add(char a, long b, int c, int d)
    
    {
    return (a + b + c + d);
    }

    push 4
    push 3
    mov edx,2 //寄存器传参
    mov cl,1 //寄存器传参
    call Add (11F1A60h) //子程序Add里面 平衡堆栈


    _fastcall调用约定不同编译器传参不同:
    1. MS VC: 最左边2个参数依次 ecx, edx
    2. borland delphi/C++:最左边2个参数 eax, edx,ecx
    3. watcom C: 最左边2个参数 eax, edx,ebx,ecx
    4. 剩下的参数的全部esp传参


    thiscall 也是寄存器传递参数, C++非静态成员函数的默认调用约定,
    对象的每个成员函数隐含接受this指针。通过ecx传递this指针

    class CSum
    {
    public:
    int Add(int a, int b)
    {
    return (a + b);
    }
    };
    void ClllDlg::OnBnClickedOk2()
    {
    CSum sum;
    sum.Add(1, 2);
    }

    push 2
    push 1
    lea ecx,[sum]
    call CSum::Add (1221B10h)


    2. 函数返回值:

    2.1函数返回值一般在eax中,如果返回值超过了eax容量,高32位存放在edx中

    2.2.通过参数按传引用的方式返回值

    以下代码就是通过参数按传引用的方式返回值
    void max(int *a, int *b);
    int main( )
    {
    int a=5,b=6;
    max(&a, &b);
    printf("a、b中较大的数是%d",a); //将最大的数显示出来
    return 0;
    }
    void max( int *a, int *b)
    {
    if(*a < *b)
    *a=*b; //经比较后,将较大的数放到a变量之中
    }


    3. 局部变量


    3.1.利用堆栈存放局部变量,分配局部变量空间

    形式1: 形式2:   形式3: 
    sub esp, n
    add esp, n
    add esp,-n
    sub esp, -n

    push reg
    pop reg

    参数相对于 ebp是正的

    局部变量相对于 esp是负的

    3.2.利用寄存器存放局部变量

     除了堆栈存放 局部变量外,6个通用寄存器也可以存放局部变量。

    4. 全局变量
    1. 全局变量一般存放在.data区块, 它是一个固定值,采用硬编码方式
    mov dword ptr [4944A0h],7 //4944A0h就是全部变量的地址
    mov eax,dword ptr [b]

    5. 数组
    5.1. 数组是连续数据的集合,汇编下访问数组是基址+变量的形式访问

    #include <stdio.h>
    int main(void)
    {
    static int a[3]={0x11,0x22,0x33};
    int i,s=0,b[3];
    for(i=0;i<3;i++)
    { 
    s=s+a[i];
    b[i]=s;
    }
    
    for(i=0;i<3;i++)
    {
    printf("%d
    ",b[i]);
    }
    return 0;
    }

    mov eax,dword ptr [i]
    mov ecx,dword ptr [s]
    add ecx,dword ptr a (493078h)[eax*4]//493078h数组基址

     6.虚函数

    虚函数是在程序运行时刻定义的函数, 虚函数的地址不能在编译时刻确定,所以虚函数引用
    通常放在一个专有数组里-虚函数表(VTBL), 每一个虚函数对象都具有虚函数表指针(VPTR),
    虚函数通过指向虚函数表的指针间接调用。

    #include <stdio.h>
    class CSum
    {
    public:
    	virtual int Add(int a, int b) 
    	{
    		return(a + b);
    	}
    	virtual	int	Sub(int a, int b )
    	{
    		return(a - b);
    	}
    };
    void main()
    {   
    	CSum*	pCSum = new CSum ;  
    	pCSum->Add(1,2);
    	pCSum->Sub(1,2);
    } 
    

      

    .text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
    .text:00401000 _main proc near ; CODE XREF: start+AFp
    .text:00401000
    .text:00401000 argc = dword ptr 4
    .text:00401000 argv = dword ptr 8
    .text:00401000 envp = dword ptr 0Ch
    .text:00401000
    .text:00401000 push esi
    .text:00401001 push 4
    .text:00401003 call ??2@YAPAXI@Z ;new为创建对象分配4个字节
    .text:00401008 add esp, 4
    .text:0040100B test eax, eax
    .text:0040100D jz short loc_401019
    .text:0040100F mov dword ptr [eax], offset off_4050A0 ;将4050A0写入创建对象的实例中,4050A0是虚函数表的指针。
    .text:00401015 mov esi, eax
    .text:00401017 jmp short loc_40101B
    .text:00401019 ; ---------------------------------------------------------------------------
    .text:00401019
    .text:00401019 loc_401019: ; CODE XREF: _main+Dj
    .text:00401019 xor esi, esi
    .text:0040101B
    .text:0040101B loc_40101B: ; CODE XREF: _main+17j
    .text:0040101B mov eax, [esi]
    .text:0040101D push 2
    .text:0040101F push 1
    .text:00401021 mov ecx, esi
    .text:00401023 call dword ptr [eax]
    .text:00401025 mov edx, [esi]
    .text:00401027 push 2
    .text:00401029 push 1
    .text:0040102B mov ecx, esi
    .text:0040102D call dword ptr [edx+4]
    .text:00401030 pop esi
    .text:00401031 retn
    .text:00401031 _main endp

    查看虚函数表 4050A0的数据
    004050A0 40 10 40 00 50 10 40 00 FF FF FF FF 2E 11 40 00 @.@.P.@.......@.
    004050B0 42 11 40 00 5F 5F 47 4C 4F 42 41 4C 5F 48 45 41 B.@.__GLOBAL_HEA

    VTBL的2组数据
    [VTBL] = 401040h
    [VTBL+4] = 401050h
    进一步看看这2个指针的内容

    sub_401040 proc near
    arg_0= dword ptr 4
    arg_4= dword ptr 8
    mov eax, [esp+arg_4]
    mov ecx, [esp+arg_0]
    add eax, ecx
    retn 8
    sub_401040 endp


    sub_401050 proc near
    arg_0= dword ptr 4
    arg_4= dword ptr 8
    mov eax, [esp+arg_0]
    mov ecx, [esp+arg_4]
    sub eax, ecx
    retn 8
    sub_401050 endp

    原来 虚函数是通过虚函数表的指针 间接调用

  • 相关阅读:
    88. 合并两个有序数组
    680. 验证回文字符串 Ⅱ
    345. 反转字符串中的元音字母
    633. 平方数之和
    分支程序设计
    scanf函数(初学者)
    输入与输出(初学者)
    C语句详细(初学者)
    算术运算符和算术表达式(初学者)
    变量赋值(初学者)
  • 原文地址:https://www.cnblogs.com/mayingkun/p/6060410.html
Copyright © 2011-2022 走看看