c语言逆向基础

原创 2015年11月21日 12:00:45

0x00 全局变量

  • 整数(00x12345678)在内存中的表示方式:
    大端机:逻辑上低字节放在内存低地址(高高低低原则),78 56 34 12.Intel x86
    小端机:逻辑上高字节放在内存低地址,12 34 56 78.PowerPC
  • 全局变量的线性地址在编译时已经写定了,故每个进程中同一个全局变量的线性地址是相同的。
    这里写图片描述
  • 在CPU的保护模式下,对于同一个程序的每个进程都有自己独立的线性地址空间(0—4GB),这种机制叫虚拟系统
    这里写图片描述
    注:寄存处—>进程 仓库—>物理存储系统
    寄存处编号—>线性地址 仓库编号—>物理内存地址
  • 程序访问内存用的是线性地址(mov指令中的地址就是线性地址,用户态程序无法直接访问物理内存), 虚拟管理系统将该地址转换成对应物理地址进行访问。
  • 同一程序两个进程的线性地址相同,但对应物理内存是不同的。

0x01 指针

  • 作用:地址、类型
    指针类型信息(读写字节大小)不放在指针变量中,而是放在了与该地址相关的赋值语句中,即mov指令中dword指明指针类型。
int *pi;    int gi;  pi = &gi;
00C6139E  mov   dword ptr ds:[00C67138h],0C6713Ch  ;pi存着gi地址
*pi = 12;
00C613A8  mov   eax,dword ptr ds:[00C67138h];将pi中存的gi地址给eax
00C613AD  mov   dword ptr [eax],0Ch  ;将0Ch赋给gi
  • 注:&pi = 00C67138h 、 &gi = 0C6713Ch

  • 指针强制转换
    强制转换并没有实际的指令,转换的影响不是发生在转换的时候,而是用转换后的身份去访问内存时体现在指令中的。
    eg:int i; int *pi; short *ps; char *pc;
    这里写图片描述
    运行17行后:
    这里写图片描述
    运行18行后:
    这里写图片描述
    运行19行后:
    这里写图片描述

0x02 函数调用和局部变量

1) 函数调用

int Add(int x,int y)
{
    int sum;
    sum = x+y;
    return sum;
}
void main()
{
    int z;
    z = Add(1,2);
    printf("%d\n",z);
}
  • 反汇编代码
    这里写图片描述

  • call指令由相对定位进行跳转:跳转到子函数的 = call指令下一条指令起始地址 + 偏移量
    call指令地址 + call指令长度(5) + 偏移量

  • 偏移量:fffff99b为负数补码,原整数 = -665h。数值位取反加1
    故00d511c2h = 00d51822h +5h -665h
  • 每次压栈,32位机用4字节存储栈值。X86中,push指令使ESP的值减4
  • call指令功能:a) push 返回地址 b) jump 函数入口地址

2) 参数传递

  • 参数地址 = EBP + 偏移量
  • 局部变量地址 = EBP – 偏移量
    Visual C++6.0中第一个局部变量地址为EBP-4,VS2005版本开始则为EBP – 8,这是因为吸取了Stack Guard的溢出攻击防护机制
    这里写图片描述

3) 函数调用返回

  • ret指令功能:相当于pop eip,将栈顶保存的返回地址弹入EIP

4) 函数返回值

  • 一般将返回值存在EAX中,函数在执行ret指令前mov eax , 返回值
    这里写图片描述
  • 子函数返回值较小时,将其先保存到母函数栈帧局部变量区临时内存中,然后再赋给母函数局部变量
  • 子函数返回值较大时,先分配一段内存用于保存返回值,并将该内存地址传给子函数,将返回值存入该内存,母函数通过该地址获取返回值

5) 平衡栈

  • 调用方清栈,call返回后执行add esp,x
  • 被调方清栈,执行ret x
    前者用空间代价换取了变参功能

0x03 函数指针

  • 函数指针成功赋值的前提:参数表、调用惯例、返回类型一致
  • 函数指针的作用:让函数指针指向不同函数,实现同样的调用代码调用不同的函数。相当于面向对象中虚函数的作用
int(*pfunc)(int,int);
int _cdecl add(int x,int y)
{
    int sum;
    sum = x + Y;
    return sum;
}
void main(0
{
    pfunc = add;
    pfunc = &add;
    pfunc(1,2);
    add(1,2);
}
  • 反汇编代码
    这里写图片描述
  • 函数指针不能强制转换

0x04 数组

  • 用首部地址+偏移量的形式访问
void main()
{
    int array[5];
    int i = 0;
    for(i;i<5;i++)
    {
        array[i] = i;
    }
    for(i = 0;i <7;i++)
    {
        printf("%d ",array[i]);
    }
}
  • 反汇编代码
    这里写图片描述
  • 变量在栈中的分布
    这里写图片描述
  • 无法从反编译结果看出某块内存是结构体还是相邻的局部变量

0x05 对齐

  • 对齐:基本数据(如单字节、双字节、四字节整数)存放处的地址必须能被自己数据类型的大小整除。若不满足要求,不同体系结构的CPU反应不同。有的直接结束进程的执行,有的访问速度变慢(x86)。
  • 结构体的对齐
    首先选定一个盒子,依次将字段往盒子中放,盒子放不下后,用下一个盒子存放
    限制条件:
    a)盒子长度 = min{max{sizeof(成员变量)},对齐长度}
    b)字段放入位置:离盒子头部偏移 = n *sizeof(成员变量) n = 0,1,2,….
    c)适用VS编译器
  • eg:
    当对齐长度为4时,sizeof(Person) = 12
    这里写图片描述
struct Person   
{
    char c1;
    short s;            
    char c2;
    int i;
};
  • 网络编程中,协议头部一般用结构体表示,在自己手动构造数据包强制转换并赋给头部结构体时应注意对齐问题
struct ip_hdr
{
    char version;
    char ihl;
    unsigned char tos;
    unsigned short tot_len;
    unsigned short id;
    unsigned short frag_off;
    unsigned char ttl:
    unsigned char protocol;
    unsigned short check;
    unsigned int saddr;
    unsigned int daddr;
}

发送方:

char buf[128];
int * pdataLen;
buf[0] = 1;         //设定标志类型
pdataLen = (int *)&buf[1];
*pdataLen = htonl(12);      //设定数据长度
//填充数据部分12字节
buf[5] = 1;
buf[6] = 2;
...
buf[16] = 12;
...
//将buf开始的17个字节发送出去,包括头部5字节,数据12字节
send(sockethnd,buf,5 + 12,0);

接收方:

struct hdr* phdr;
char buf[128];
int dataLen;
recv(sockethnd,buf,5,0);
phdr = (struct hdr*)buf;
printf("hdr flag is %\n",dataLen);

第五行指针赋值过程会因为结构体的对齐出现问题。
解决办法:
a) 调整结构体成员变量的顺序达到对齐
b) 填充reserved字段以达到对齐

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

逆向还原C语言代码 练习1

找了个C语言100列 用来练习OD还原C语言代码 至于C+的 以后也会写 今天的课件下载 链接: http://pan.baidu.com/s/1nvApltJ 密码: sgur 我们先来看第...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

反汇编 c++/c 确定main函数的位置

根据启动函数确定winmain/main函数的入口地址

代码逆向(一)——寻找main函数入口

逆向的第一步是什么?这要问你学习C语言的第一步是什么,很自然的,逆向的第一步当然也是大名鼎鼎“HelloWorld!”了。但是也不要因此就误认为这一节会很简单,如果你是第一次接触逆向的话,那么这一节还...

C语言MySQL 基础操作(添加、查询和删除)

MySQL 之数据添加 /*练习mysql数据库的查询*/ #include #include #include "/usr/local/mysql/include/mysql.h" int...

黑马程序员——C语言基础篇---宏定义、数组、字符串和函数

通过一道题来解析宏定义、数组、字符串和函数

C语言基础 一次把整个int数组初始化为0的方式

镇场诗:慈心积善融学习,技术誓为大家学。善心速造多好事,前人栽树后乘凉。我今于此写经验,愿见文者得启发。 ///////////////////////////////////////////////...

C语言基础学习基本数据类型-浮点型

c语言里的浮点型数据类型
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:c语言逆向基础
举报原因:
原因补充:

(最多只允许输入30个字)