转:Hide data inside pointers(在指针中隐藏数据)

该文介绍了如何使用指针中一些未使用的位来隐藏一些数据。

When we write C code, pointers are everywhere. We can make a little extra use of pointers and sneak in some extra information in them. To pull this trick off, we exploit the natural alignment of data in memory.

Data in memory is not stored at any arbitrary address. The processor always reads memory in chunks at the same size as its word size; and thus for efficiency reasons, the compiler assigns addresses to entities in memory as multiples of their size in bytes. Therefore on a 32 bit processor, a 4-byte int will definitely reside at a memory address that is evenly divisible by 4.

Here, I am going to assume a system where size of int and size of pointer are 4 bytes.

Now let us consider a pointer to an int. As said above, the int can be located at memory addresses 0x1000 or 0x1004 or 0x1008, but never at 0x1001 or 0x1002 or 0x1003 or any other address that is not divisible by 4.
Now, any binary number which is a multiple of 4 will end with 00.
This essentially means that for any pointer to an int, its 2 lower order bits are always zero.

Now we have 2 bits which communicate nothing. The trick here is put our data into these 2 bits, use them whenever we want and then remove them before we make any memory access by dereferencing the pointer.

Since bitwise operations on pointers don’t go well with the C standard, We will be storing the pointer as an unsigned int.

The following is a naive snippet of the code for brevity. See my github repo - hide-data-in-ptr for the full code.

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: %u\n", p);

    put_data(&p, 3);

    printf("ptr with data: %u\n", p);
    printf("data stored in ptr: %u\n", get_data(p));

    cleanse_pointer(&p);

    printf("Cleansed ptr: %u\n", p);
    printf("Dereferencing cleansed ptr: %u\n", *(int*)p);

    return 0;
}

This will give the following output:

Original ptr:  3216722220
ptr with data: 3216722223
data stored in ptr: 3
Cleansed ptr:  3216722220
Dereferencing cleansed ptr: 701

We can store any number that can be represented by 2 bits in the pointer. Using put_data(), the last 2 bits of the pointer are set as the data to be stored. This data is accessed using get_data(). Here, all bits except the last 2 bits are overwritten as zeroes there by revealing our hidden data.

cleanse_pointer() zeroes out the last 2 bits, making the pointer safe for dereferencing. Note that while some CPUs like Intel will let us access unaligned memory locations, certain others like ARM CPU will fault. So, always remember to keep the pointer pointed to an aligned location before dereferencing.

Is this used anywhere in the real world?

Yes, it is. See the implementation of Red Black Trees in the Linux kernel (link).

The node of the tree is defined using:

struct rb_node {
    unsigned long  __rb_parent_color;
    struct rb_node *rb_right;
    struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));

Here unsigned long __rb_parent_color stores:

1. the address of the parent node

2. the node’s color.

The color is represented as 0 for Red and 1 for Black.

Just like in the earlier example, this data is sneaked into the ‘useless’ bits of the parent pointer.

Now see, how the parent pointer and the color information is accessed:

/* 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)
时间: 2024-11-05 19:21:28

转:Hide data inside pointers(在指针中隐藏数据)的相关文章

在EXCEL中隐藏数据

我们在excel工作表中输入数据的某些数据,由于某种原因需要把他们隐藏起来.大家在日常工作当中大多都使用过隐藏列或者隐藏行.大家先看一下今天用的原始数据. 我们现在要隐藏年龄和Tom的所有信息.这个操作很简单,就是隐藏B列和第三行. 结果如下图,大家看一下效果不错吧,但是这等操作何必难道这里呢? 请看,大家还是能看出B列不见了,第三行不见了.还是能被别人看出来,你隐藏数据的意图是. 再来看看下图: 什么都没有,数据还是图一中的原始数据.而且这种隐藏,第一眼看上去是看不出有数据的. 隐藏数据操作如

获取剪贴板中的数据

/// <summary> /// Adds the specified window to the chain of clipboard viewers. Clipboard viewer windows receive a WM_DRAWCLIPBOARD message whenever the content of the clipboard changes. This function is used for backward compatibility with earlier v

微服务开发中的数据架构设计

本文来自作者 陈伟荣 在 GitChat 分享的文章[微服务开发中的数据架构设计] 前言 微服务是当前非常流行的技术框架,通过服务的小型化.原子化以及分布式架构的弹性伸缩和高可用性,可以实现业务之间的松耦合.业务的灵活调整组合以及系统的高可用性.为业务创新和业务持续提供了一个良好的基础平台.本文分享在这种技术架构下的数据架构的设计思想以及设计要点,本文包括下面若干内容. 微服务技术框架中的多层数据架构设计 数据架构设计中的要点 要点1:数据易用性 要点2:主.副数据及数据解耦 要点3:分库分表

访问cv::Mat中的数据时遇到的指针类型问题

在用Opencv的时候由于下图原本的图像尺寸是1111*1111,要进行resize,代码如下: cv::Mat img = cv::imread("//Users//apple//td3//vase//19201.png",CV_LOAD_IMAGE_GRAYSCALE); cv::Mat img2; cv::resize(img, img2, cv::Size(400,400),0,0, cv::INTER_AREA); 因为我根本不知道img的数据是什么类型(不知道数据类型根本无

使用jQuery的data方法来为页面中的某个元素存储数据,(获取焦点,清除默认值)

使用data方法可以避免在DOM中存储数据,有些前端开发er喜欢使用HTML的属性来存储数据: 使用”alt”属性来作为参数名存储数据其实对于HTML来说是不符合语义的. 我们可以使用jQuery的data方法来为页面中的某个元素存储数据: html部分: 1 <form id="testform"> 2 <input type="text" class="clear" value="Always cleared&qu

报错:此版本的SQL Server Data Tools与此计算机中安装的数据库运行时组件不兼容

在Visual Studio 2012中使用Entity Framework,根据模型生成数据库时,报如下错误: 无法在自定义编辑器中打开Transact-SQL文件此版本的SQL Server Data Tools与此计算机中安装的数据库运行时组件不兼容 解决办法:下载"Server Data Tools for Visual Studio 2012" ,并安装,重新启动Visual Studio 2012.

C++ 如何从指针中得到类型或引用

C++标准语法中我们可以通过  * 来定义指针 &来指定引用 如 typedef int * _INT_PTR; 我们定义了一个指针 typedef int& _INT_REF; 我们定义了一个引用 那我们有没办法通过一个已知的指针或引用来定义一个原始类型呢? typedef *_INT_PTR  _INT? 这似乎不符合语法. 那你可能会问这么奇怪的需求有存在的需要吗? 当你操作别人的数据时,对方由于一些原因只给你提供了指针类型(如后本人所碰到的),你就有可能有这样的需求. 这样的需求有

TCP报文格式和三次握手——三次握手三个tcp包(header+data),此外,TCP 报文段中的数据部分是可选的,在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。

from:https://blog.csdn.net/mary19920410/article/details/58030147 TCP报文是TCP层传输的数据单元,也叫报文段. 1.端口号:用来标识同一台计算机的不同的应用进程. 1)源端口:源端口和IP地址的作用是标识报文的返回地址. 2)目的端口:端口指明接收方计算机上的应用程序接口. TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接. 2.序号和确认号:是TCP可靠传输的关键部分.序号是本报文段发送

读取 Amazon Kinesis Data Streams 中的数据

使用者 是一种处理 Kinesis 数据流中的所有数据的应用程序.当使用者使用增强型扇出功能 时,它会获取其自己的 2 MiB/秒的读取吞吐量配额,从而允许多个使用者并行读取相同流中的数据,而不必与其他使用者争用读取吞吐量.默认情况下,流中的每个分片均提供 2 MiB/秒的读取吞吐量.此吞吐量跨正在从某给定分片进行读取的所有使用器获取分片.换言之,每个分片的默认 2 MiB/秒的吞吐量是固定的,即使有多个使用器正在从分片中进行读取. 特性 没有增强型扇出功能的未注册使用者 具有增强型扇出功能的注