继承更新个人的学习笔记,
其它笔记传送门
逆向根本笔记一 进制篇
逆向根本笔记二 数据宽度和逻辑运算
逆向根本笔记三 通用寄存器和内存读写
逆向根本笔记四 堆栈篇
逆向根本笔记五 标志寄存器
逆向根本笔记六 汇编跳转和比较指令
逆向根本笔记七 堆栈图(重点)
逆向根本笔记八 反汇编分析C语言
逆向根本笔记九 C语言内联汇编和调用协定
逆向根本笔记十 汇编寻找C程序入口
逆向根本笔记十二 汇编 全局和局部 变量
逆向根本笔记十三 汇编C语言类型转换
逆向根本笔记十四 汇编嵌套if else
逆向根本笔记十五 汇编比较三种循环
逆向根本笔记十六 汇编一维数组
逆向根本笔记十七 汇编二维数组 位移 乘法
逆向根本笔记十八 汇编 结构体和内存对齐
逆向根本笔记十九 汇编switch比较if else
逆向根本笔记二十 汇编 指针(一)
逆向根本笔记二十一 汇编 指针(二)
逆向根本笔记二十二 汇编 指针(三)
逆向根本笔记二十三 汇编 指针(四)
逆向根本笔记二十四 汇编 指针(五) 系列完结
C语言根本类型
差别于寻常的C语言根本数据类型的学习,这里以汇编的形式 来学习差别数据类型的存储方式和差异
C语言的数据类型
C语言的根本类型属于C语言的数据类型的一部门:
这里先从最简单的根本类型举行入手学习,在学习根本类型之前再温故一下先前学习过的汇编的数据类型
汇编的数据类型
数据类型名称位BYTE字节8BITWORD字=2字节16BITDWORD双字=4字节32BIT整数类型
C语言的整数类型有:char short int long
整数类型位字节对应汇编char8BIT1字节byteshort16BIT2字节wordint32BIT4字节dwordlong32BIT4字节dword大家可能会觉得疑惑,为什么int和long所表现的貌似一样?
这实在是历史遗留问题 ,在以前的16位计算机 中,int的长度位2字节,但是在32位计算机 中,int类型变成了4字节,而long类型原来便是4字节,现在在仍旧是4字节
存储方式
接下来从汇编的角度来看看整数类型如何存储
#include "stdafx.h"int main(int argc, char* argv[]){ char a=0xFF; short b=0xFF; int c=0xFF; long d=0xFF; return 0;}
语句对应汇编单元char a=0xFF;mov byte ptr [ebp-4],0FFhbyteshort b=0xFF;mov word ptr [ebp-8],offset main+20h (0040d4e0)wordint c=0xFF;mov dword ptr [ebp-0Ch],0FFhdwordlong d=0xFF;mov dword ptr [ebp-10h],0FFhdword超出数据宽度赋值
上面的赋值都是在根本类型的数据宽度之内,那么如果超出数据宽度会如何?
修改赋值的内容为0x123456789
#include "stdafx.h"int main(int argc, char* argv[]){ char a=0x123456789; short b=0x123456789; int c=0x123456789; long d=0x123456789; return 0;}然后再观察反汇编代码
语句对应汇编现实赋值单元char a=0x123456789;mov byte ptr [ebp-4],89h89hbyteshort b=0x123456789;mov word ptr [ebp-8],offset main+20h (0040d4e0)6789hwordint c=0x123456789;mov dword ptr [ebp-0Ch],23456789h23456789hdwordlong d=0x123456789;mov dword ptr [ebp-10h],23456789h23456789hdword不难发现,所有赋值语句全部都高位截断 了,即高位的部门全部舍去,只赋值了数据的低位
有符号数和无符号数分析
整数类型分为有符号(signed)和无符号(unsigned)两种
默认就是有符号的类型
通过汇编观察有符号和无符号在内存中存储时是否有差别,这里以char 为例
#include "stdafx.h"int main(int argc, char* argv[]){ char signed a=0xFF; char unsigned b=0xFF; return 0;}
我们发现无符号数和有符号数在内存存储中并无差别 ,再一次印证了我们前面所学的:计算机并不关心数据是有符号数还是无符号数,决定一个数据是有符号数还是无符号数的是使用数据的我们,同一个数据使用差别的方式来解析
了解了有符号数和无符号数的本质后,再来谈谈有符号数和无符号数的注意事项
有符号数和无符号数注意场景
比较大小
同为有符号数时
#include "stdafx.h"int main(int argc, char* argv[]){ char a=0xFF; char b=1; if(a>b){ printf("a>b\n"); }else if(ab){ printf("a>b\n"); }else if(ab){ printf("a>b\n");}
比较无符号数
char unsigned a=0xFF;char unsigned b=1;if(a>b){ printf("a>b\n");}
我们发现这里的使用的jcc语句都是jle:jump less equal 小于等于才跳转(有符号数),和我们的a>b恰好相反
但是我们会发现在有符号数那里使用了movsx指令,该指令为汇编语言数据传送指令MOV的变体。带符号扩展 ,并传送。
即它会将char从原本的byte扩展到dword,如许一来数据长度被扩展以后,天然就可以使用同一个jle指令来举行比较
int的比较
前面char的比较是通过数据宽度的扩展 来实现比较的,那么当使用int时,无法扩展符号的数据宽度时,如何比较?
将上面的char改为int后再次观察反汇编代码
比较有符号数
int a=0xFF;int b=1;if(a>b){ printf("a>b\n");}
比较无符号数
int unsigned a=0xFF;int unsigned b=1;if(a>b){ printf("a>b\n");}
差别
我们可以发现,同样是比较,比较无符号数和有符号数时,分别对应两个差别的jcc语句
比较jcc语句含义比较有符号数jle main+3Dh(0040d7ed)小于等于则跳转 (有符号数)比较无符号数jbe main+3Dh(0040d7ed)小于等于则跳转 (无符号数)浮点类型
C语言的浮点类型分为float和double
存储方式和规范
float和double在存储方式上都是遵从IEEE规范 的
float的存储方式如下图所示:
double的存储方式如下图所示:
由于double的长度比较长,我们下面就用float作为例子,现实上,double不过是比float精度更高了,在将浮点数转化 为存储到内存中的二进制的步调险些一致
转化步调
将一个float型转化为内存存储格式的步调为:
先将这个实数的绝对值化为二进制格式
将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边
从小数点右边第一位开始数出二十三位数字放入第22到第0位。
如果实数是正的,则在第31位放入“0”,否则放入“1”
如果n是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”
如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。 如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位
看起来很复杂QAQ,但联合下面的实例来看就还好(。・∀・)ノ゙
在转化步调中的第一步,又分为整数部门的二进制化和小数部门的二进制化
十进制整数二进制化
采用除留余数法
比如将11转化成二进制数
计算余数11/2=515/2=212/2=101/2=010结束11二进制表现为(从下往上):1011
注意到只要除以后结果为0便结束了,恣意整数不断除以2最终都会等于0,因此所有整数都可以用二进制来精确表现
十进制小数二进制化
采用乘二取整法
比如将0.9转化为二进制数
计算取整数部门0.9*2=1.810.8(前面结果的小数部门)*2=1.610.6*2=1.210.2*2=0.400.4*2=0.800.8*2=1.610.6*2=1.21…………0.9二进制表现为(从上往下):110011……
很显然,上面的计算过程循环了,也就是说*2永远不可能扫除小数部门 ,如许算法将无穷下去。很显然,并非所有的小数都可以用二进制精确表现 ,就和十进制里也无法精确表现出1/3一样
实例
8.25f
#include "stdafx.h"int main(int argc, char* argv[]){ float i=8.25f; return 0;}我们用反汇编查看8.25f
00401028 mov dword ptr [ebp-4],41040000h我们会发现8.25f的表现形式为41040000h,接下来我们就来研究这个41040000h是怎么来的
一步步按先前提到的转化步调来:
实数的绝对值化为二进制格式
先处理整数部门8 .25
计算余数8/2=404/2=202/2=101/2=01整数部门8转化为二进制:1000(从下往上)
再处理小数部门8.25
计算取整数部门0.25*2=0.500.5*2=1.010.25转化为二进制可表现为:01(从上往下)
于是8.25用二进制可表现为1000.01
填充尾数
接下来先填充尾数部门
先将二进制数转为用科学计数法 表现
1000.01=1.00001*2的3次方 (小数点向左移动 3位 指数 为3)
1.**00001***2
就是将小数点背面的23位填入尾数部门,我们这里小数部门恰好能够精确 地转化为二进制数,于是剩下的部门用0填充 即可
如果是前面的0.9转化为的0.110011……=1.1100……*2的-1次方
则是截取 小数点背面的23位填入1100……
符号位指数部门尾数部门占用空间1823存储内容00001 000000000000000000此时尾部部门已经填充完毕,再填充符号位
填充符号位
符号位就简单多了,正数填充0,负数填充1即可
这里我们的8.5f是正数,填0结束
符号位指数部门尾数部门占用空间1823存储内容000001 000000000000000000填充指数部门
指数部门的最高位填充看前面是前面将二进制数科学计数法化时,是举行了左移还是右移,左移填1,右移填0
前面我们得到了8.5f的转化:
1000.01=1.00001*2的3次方 (小数点向左移动 3位 指数 为3)
很明显我们的是左移,因此,最高位填写1
剩下的部门则是用指数减去1后二进制化填充即可
8.5f的指数为3,3-1=2
2的二进制为10
以是指数部门应该为:
1 0000010
于是整个填充完成
符号位指数部门尾数部门占用空间1823存储内容01 0000010 00001 000000000000000000总共为0100 0001 0000 0100 0000 0000 0000 0000
转化为十六进制为4 1 0 4 0 0 0 0 0
和我们前面用汇编看到的结果一致
-8.25f
#include "stdafx.h"int main(int argc, char* argv[]){ float i=-8.25f; return 0;}
00401028 mov dword ptr [ebp-4],0C1040000h-8.25f和8.25f相差的只有符号位,于是将前面的8.25f的符号位改为1即可
符号位指数部门尾数部门占用空间1823存储内容1 1 0000010 00001 000000000000000000也就是1100 0001 0000 0100 0000 0000 0000 0000
转为十六进制是 C 1 0 4 0 0 0 0
和前面反汇编看到的结果一致
0.25f
这次看个纯小数 在内存中如何存储,依旧采取相同的套路
实数的绝对值化为二进制格式
计算取整数部门0.25*2=0.500.5*1=1.010.25f转化为二进制:0.01
填充尾数
科学计数法表现:0.01=1.0*2的-2次方 (小数点向右移动 2位 指数 为-2 )
尾数填充0,1.0 小数点背面全为0,于是尾数全部填0即可
符号位指数部门尾数部门占用空间1823存储内容0 0000000000000000000000填充符号位
0.25f是正数直接填充0即可
符号位指数部门尾数部门占用空间1823存储内容00 0000000000000000000000填充指数部门
科学计数法表现:0.01=1.0*2的-2次方 (小数点向右移动 2位 指数 为-2 )
向右移动,指数部门最高位填0
指数为-2,-2-1=-3
将-3转化为二进制:-3的十六进制对应fd,fd转为二进制:1111 1101
不知道为什么-3对应fd的可以看这里:逆向根本笔记二 数据宽度和逻辑运算
我们发现-3转为二进制共有8位,但是指数部门总共也只有8位,其中最高位还用作表现是右移填充了0,于是只截取低7位填充进指数部门剩下的内容
于是整个填充完成
符号位指数部门尾数部门占用空间1823存储内容001111101 0 0000000000000000000000总共为0011 1110 1000 0000 0000 0000 0000 0000
转化为十六进制为3 e 8 0 0 0 0 0
接下来用反汇编验证一下:
得到的结果是一致的,好耶ヽ(✿゚▽゚)ノ
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
楼主热帖