如何在指针中隐藏数据?
2015-01-29 14:48
288 查看
编写 C 语言代码时,指针无处不在。我们可以稍微额外利用指针,在它们内部暗中存储一些额外信息。为实现这一技巧,我们利用了数据在内存中的自然对齐特性。
内存中的数据并非保存在任意地址。处理器通常按照其字大小相同的块读取内存数据;那么考虑到效率因素,编译器会按照块大小的整数倍对内存中的实体进行地址对齐。因此在32位的处理器上,一个4字节整型数据肯定存放在内存地址能被4整除的地方。
下面,假设系统中整型数据和指针大小均为 4 字节。
现在有一个指向整型的指针。如上所述,整型数据可以存放在内存地址0×1000或者0×1004或者0×1008,但是决不会存放在 0×1001或者0×1002或者0×1003或者其他不能被4整除的任何地址。所有是4整数倍的二进制数都是以00结尾。实际上,这意味着对于所有指向 整型的指针,它的最后两位总是0。
那么有2比特没有承载任何信息。此处的技巧是将我们的数据放置到这两个比特中,在需要时使用,并在通过指针解引用来访问内存前删除它们。
由于C标准对指针位操作的支持不是很好,所以我们将指针保存为一个无符号整型数据。
下面是一段简短的简单代码片段。完整的代码查看github代码仓库中的 hide-data-in-ptr 。
代码输出如下:
我们可以在指针中存储任何可以用两个比特位表示的数据。使用put_data()函数,设置指针的最低两位为要存储的数据。该数据可以使用get_data()函数获取。此处除了最后两位所有的位都被覆盖为零,于是我们隐藏的数据就显示出来。
cleanse_pointer()函数将最低两位置零,保证指针安全地解引用。注意虽然有些CPU像Intel允许我们访问未对齐内存地址,但其余CPU像ARM会出现访问错误。所以,要牢记在解引用前保证指针指向已对齐内存地址。
树的结点定义如下:
此处unsigned long __rb_parent_color存储了如下信息:
父节点的地址
结点的颜色
色彩的表示用0代表红色,1代表黑色。
和前面的例子一样,该数据隐藏在父指针“无用的”比特位中。
下面看一下父指针和色彩信息是如何获取的:
内存中每一比特都很珍贵,砸门永远不要浪费。——(本文作者)
本文来自:Linux学习教程网
内存中的数据并非保存在任意地址。处理器通常按照其字大小相同的块读取内存数据;那么考虑到效率因素,编译器会按照块大小的整数倍对内存中的实体进行地址对齐。因此在32位的处理器上,一个4字节整型数据肯定存放在内存地址能被4整除的地方。
下面,假设系统中整型数据和指针大小均为 4 字节。
现在有一个指向整型的指针。如上所述,整型数据可以存放在内存地址0×1000或者0×1004或者0×1008,但是决不会存放在 0×1001或者0×1002或者0×1003或者其他不能被4整除的任何地址。所有是4整数倍的二进制数都是以00结尾。实际上,这意味着对于所有指向 整型的指针,它的最后两位总是0。
那么有2比特没有承载任何信息。此处的技巧是将我们的数据放置到这两个比特中,在需要时使用,并在通过指针解引用来访问内存前删除它们。
由于C标准对指针位操作的支持不是很好,所以我们将指针保存为一个无符号整型数据。
下面是一段简短的简单代码片段。完整的代码查看github代码仓库中的 hide-data-in-ptr 。
void put_data(int *p, unsigned int data) { assert(data < 4); *p |= data; } unsigned int get_data(unsigned int p) { return (p & 3); } void cleanse_pointer(int *p) { *p &= ~3; } int main(void) { unsigned int x = 701; unsigned int p = (unsigned int) &x; printf("Original ptr: %un", p); put_data(&p, 3); printf("ptr with data: %un", p); printf("data stored in ptr: %un", get_data(p)); cleanse_pointer(&p); printf("Cleansed ptr: %un", p); printf("Dereferencing cleansed ptr: %un", *(int*)p); return 0; }
代码输出如下:
Original ptr: 3216722220 ptr with data: 3216722223 data stored in ptr: 3 Cleansed ptr: 3216722220 Dereferencing cleansed ptr: 701
我们可以在指针中存储任何可以用两个比特位表示的数据。使用put_data()函数,设置指针的最低两位为要存储的数据。该数据可以使用get_data()函数获取。此处除了最后两位所有的位都被覆盖为零,于是我们隐藏的数据就显示出来。
cleanse_pointer()函数将最低两位置零,保证指针安全地解引用。注意虽然有些CPU像Intel允许我们访问未对齐内存地址,但其余CPU像ARM会出现访问错误。所以,要牢记在解引用前保证指针指向已对齐内存地址。
这在实际中有应用吗?
是的,有应用。查看Linux内核中红黑树的实现( 链接 )。树的结点定义如下:
struct rb_node { unsigned long __rb_parent_color; struct rb_node *rb_right; struct rb_node *rb_left; } __attribute__((aligned(sizeof(long))));
此处unsigned long __rb_parent_color存储了如下信息:
父节点的地址
结点的颜色
色彩的表示用0代表红色,1代表黑色。
和前面的例子一样,该数据隐藏在父指针“无用的”比特位中。
下面看一下父指针和色彩信息是如何获取的:
/* in rbtree.h */ #define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
/* in rbtree_augmented.h */ #define __rb_color(pc) ((pc) & 1) #define rb_color(rb) __rb_color((rb)->__rb_parent_color)
内存中每一比特都很珍贵,砸门永远不要浪费。——(本文作者)
本文来自:Linux学习教程网
相关文章推荐
- 如何在指针中隐藏数据?
- 如何在指针中隐藏数据?
- 如何在指针中隐藏数据
- 如何在图片中隐藏数据
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量
- opencv IplImage各参数详细介绍以及如何从一个JPEG图像数据指针转换得到IplImage
- 【Excle数据透视表】如何隐藏数据透视表中行字段的”+/-”按钮
- 【Excle数据透视表】如何显示/隐藏数据透视表字段列表
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量
- 在CUDA C/C++中如何隐藏数据传输
- 【Excle数据透视】如何隐藏数据透视表字段的分类汇总
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量
- c#无指针如何对数据产生影响。
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量(转)
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量的
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量
- 如何从一个php文件向另一个地址post数据,不用表单和隐藏的变量
- 数据隐藏之Qt中d指针详解