C++ 反编译揭秘 笔记

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yuqian123455/article/details/86742151

一、    基本数据表现形式
1.1    整数类型
整数使用二进制直接存储;
       1.1.1    有符号
第一位是符号,0表示正,1表示负;
负数:补码 = 反码 + 1;
计算机只能做加法,所以负数使用补码做加法;
       1.1.2    无符号
32位取值范围:0x00000000 ~ 0xFFFFFFFF (4294967295);
        小端方式:根据数据类型,低位数据排放在内存的低位;
        如:0x12345678  存放在内存中是 78 56 34 12 ;
        其他见《OD分析总结》。

1.2    浮点数类型
浮点数的存储先是使用二进制码进行重新编码,再进行存储;
在C、C++中,将浮点数转换成为整数的时候,采用的是舍弃小数部分。
浮点数计算不使用通用寄存器,而是使用浮点寄存器;

Float型:
    Bits    1 2 3 4 5 6 7 8 9 10 11 12 ~~~ 32
    第一位:1位,单位:0 为整数,1为负;
 第2到9 位:8位;指数部分+ 127
 第10~ 32 位:尾数部分;
 这里+127是因为指数可能是负数;IEEE规定;
例如:12.25 的存储
12.25 = 1100.01 = 1.10001 *e3
所以:
第一位:1位,单位:0 为整数,1为负;
 第2到9 位:8位;3 + 127 = 0x11 + 0x0FFFFFFF = 0x10000010
 第10~ 32 位:尾数部分1000100000…
总二进制: 0100 0001 0100 0100 ..0000..
所以:内存值为0x41440000
小端的存储个数: 00 00 44 41

1.3F 对应的是0011 1111 1100 0110 0110  …
无穷,的有误差,IEEE编码是一个近似值,存在误差,得到的是1.29 999… 
因此比较浮点数是否为0的时候,要做区间比较;
+-0.0001f 即可;

浮点数的计算是通过浮点寄存器来实现的,浮点寄存器是通过栈结构实现的,由st_0 st_7 共8个栈空间组成;
指令:FLD、FILD、FILDZ、FILD1、FTST… 直接查手册即可;
注意点:float类型作为参数的时候,被转换成双高精度浮点值;esp - 8
Printf以整形的方式输出浮点数的时候,会丢失4字节;


1.3    Bool类型
      非0 即真;
1.4    字符与字符串
两种编码:ASCII 和UNICODE
ASCII 中 Char 定义字符;
UNICODE中 wchar_t 定义字符,如果使用wchar_t 保存ASCII编码,不足补0;例如:’a’ 的ASCII 码为0x61,UNICODE为:0x0061。

汉字中的ASCII码,使用两个字节,成为GB2312-80,汉字国标,存储6763个汉字编码;UNICODE使用UCS-2编码格式;

VC中使用预编译宏TCHAR来代替它们,TCHAR会根据编译选项定义对应的字符;

字符串以结束符作为结束标志:‘\0’;

1.4    地址、指针、引用
地址:& 符号,只有变量才有地址,常量没有地址(const修饰的伪常量有地址);
指针:TYPE*本身是一种数据类型,保存各种数据类型在内存中的地址;
引用:TYPE& 

从反汇编看地址与指针:
        无论什么类型的指针都占4字节的内存,存放的是数据首地址;
        指针的取值是先取出指针保存的信息,再针对这个地址取内容,也就是一个间接寻址,这是识别指针的重要标志;
        指针 + 1: 地址 + 1*数据类型字节长;
        指针相减:得到数据类型的数据个数; 相减的指针必须是同一个类型;

从反汇编看指针与引用:
        我们常说,引用是变量的别名;实际上引用在反汇编下,没有引用这种类型。引用就是指针的一种封装; 两者没有区别;只是,引用是通过编译器实现寻址,而指针使用手动寻址;
        指针使用很容易出错,所以提倡引用;


1.6    常量 
我们常说的常量就是不能或者不希望被改变的量;
C++提供两种方式:#define  和 const
#define 是文本替换,在编译期间就替换了,真常量;
const是一种伪常量;实际上是可以被改变,使用一个指针指获取该地址,强制改变类型,就可以修改内容了; 
连接成可执行文件后就不存在了,在二进制中,也没有这两种类型存在;


二、    认识启动函数,找到用户入口
Main 或者 WinMain函数 是 语法规定的用户入口,并不是 应用程序入口;
入口函数一般是 mainCRTStartup、wmainCRTStartup、WinMainCRTStartup、wWinMainCRTStartup,具体使用哪个是编译选项决定的;
前两种是控制台下的多字节编码和UNICODE编码启动函数,后两种是Win环境下的多字节编码和UNICODE编码启动函数;
可以同callstack看到。
Getversion:运行平台的版本号;
Heap_init:初始化堆空间;
GetCommandlineA:获取命令行变量信息的首地址;
_crtGetEnvironmentStringsA : 获取环境变量的首地址;
_setargv 函数:进行命令行变量信息的参数解析;
_setenvp 函数:此函数将环境变量进行解析,放在全局的字符串里面; 
_cint 函数: 用于全局数据和 浮点寄存器的初始化;

我们也是可以重新设置入口函数的,Project - Setting -Link -Output,在EntryPoint symbol 中填写需要重新制定的函数入口,然后编译调试,可以看到确实入口改了,但是,这样的函数由于没有初始化对空间,所以会出现错误; 

如何确定main函数??OD启动的时候会停在应用程序入口,  主要在窗口中查找具有3个参数(命令行参数个数、命令行参数信息 和环境变量信息)就是main函数;

未完,待续...

展开阅读全文

没有更多推荐了,返回首页