内存中的数据存储
复习一下计算机原理的知识,整理一下数据在内存中的存储以及编码方式,主要是学习一下整数、浮点数的编码方式,以及由此导致的安全问题。水平有限,归纳不到位的地方还请指正。
知识小贴士
数据在内存中都是按照0/1来排列存储的,在内存层面是没有整数、浮点数之类的区别的;到了语言层面才会谈及这些问题:程序按照程序员声明的数据类型,读取内存,并根据预先设定好的编码方式得到相应数据的值。
存储方式
首先我们要注意一下大小端存储方式的区别,存储方式使用的是大端存储还是小段存储和使用的平台有关。
小端存储就低位数据存储在低地址,大端存储正好相反。
整数
编码
整数的编码有原码、反码、补码三类,计算机内对于整数的存储用补码表示。
上面三种编码方式都是针对signed 整数来说的,对于unsigned自然没必要折腾这些了,当然无符号整数在内存中的编码方式就是原码。
定义
三种编码方式定义如下:
对于正数来说,原码、反码以及补码是其本身;负数的原码是其本身,反码是对原码除符号位之外的各位取反,补码则是反码加1。
举例
原码: +0:0 000 0000,-0:1 000 0000
反码: +0:0 000 0000,-0: 1 111 1111
补码: +0:0 000 0000,-0: 1 111 1111+1=1 0000 0000,因为计算机会进行截断,只取低8位,所以-0的补码表示形式为0000 0000。
表示范围
可以看到,只有补码下的+0和-0表示方式是相同的,并且规定补码下的1 000 0000表示为-128(-2^n-1),表示范围较其他两中编码方式多了一个数字。
补码:-2^(n-1)~2^(n-1)-1
原码:-2^(n-1)+1~2^(n-1)-1
反码:-2^(n-1)+1~2^(n-1)-1
计算
补码可以直接带着符号位进行加减运算(不了解的可以去查查资料,网上一大堆),也是因为这个优势加上表示范围大,所以整数的编码方式采用补码的方式。
安全问题
首先先了解一下各种类型的取值范围:
类型
|
字节
|
范围
|
short int |
2byte(word)
|
0~32767(0~0x7fff) -32768~-1(0x8000~0xffff)
|
unsigned short int
|
2byte(word)
|
0~65535(0~0xffff)
|
int
|
4byte(dword) |
0~2147483647(0~0x7fffffff) -2147483648~-1(0x80000000~0xffffffff)
|
unsigned int
|
4byte(dword) |
0~4294967295(0~0xffffffff)
|
long int
|
8byte(qword)
|
正: 0~0x7fffffffffffffff 负: 0x8000000000000000~0xffffffffffffffff
|
unsigned long int
|
8byte(qword)
|
0~0xffffffffffffffff
|
关于整数溢出的东西ctf-wiki讲得很清楚,其中需要注意的就是边界、有无符号的问题,一般都是在数字范围边界处加减导致的数据错误,以及有无符号数的问题。
比如:
将-1赋值给一个unsigned 数,-1在内存中是0xffffffff(假如是int)来表示的,但是如果按照unsigned来读取的话就会变成一个很大的数;
再有就是数据在边界数加减的话会导致错误,比如无符号0xffffffff(int)+1变成0;
还有就是大范围赋值给小范围,小范围会按照低位截断来读取的,比如
long int a = 0x1000000000000000; int b = 0; b =a ;
那么最后b的值就会按照低位截断,读取a的低四个字节,最终b = 0;
wiki上面还提到了 在汇编层面,有符号是通过寄存器来运算的;而无符号是通过内存来计算的。
abs函数的经典漏洞
abs()函数通过man指令查一下用法
RETURN VALUE
Returns the absolute value of the integer argument, of the appropriate integer type for the function.
参数是一个int类型的数,返回值是参数的绝对值;
我们知道int类型的数字范围在计算机中表示的话,负数是比整数多一个的(也就是0x80000000),那么0x80000000当做参数穿进去是得不到正确的绝对值的。会出现什么样的后果呢,我们来看一下。
测试脚本:
#include <stdio.h><br>
#include <stdlib.h><br><br>
int main()<br>
{<br>
int a = -0x80000000;<br>
int b = abs(a);<br>
printf("the return value of abs(a) is : %d(10) 0x%x(16) .\n",b,b);<br><br>
return 0;<br>
}
效果:
the return value of abs(a) is : -2147483648(10) 0x80000000(16) .
可以看到,当我们输入最小的负数时,abs并不能正确处理,这个问题是计算机整数表示本身设置的问题,以后利用这个函数的时候可要记得规避这个问题。
例子
2019-qwb-babycpp其中便包含了这个漏洞的利用。
浮点数
iddm正在抽时间写o(╥﹏╥)o
reference:
https://www.cnblogs.com/knsbyoo/p/9028056.html
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/integeroverflow/intof-zh/