在介绍读取方法之前,首先介绍两种类型:unsafe.Pointer 和uintptr。
unsafe.Pointer代表的是任意类型的指针,它支持四种特有的操作:
- 任意类型的指针变量都可以转换为unsafe.Pointer
- unsafe.Pointer可以转换为任意类型的指针变量
- uintptr可以转换为unsafe.Pointer
- unsafe.Pointer可以转换为uintptr
我们知道在Go中,不同类型的指针是不可以相互转换的,但是根据以上的特性,就可以借助unsafe.Pointer作为跳板实现不同类型的指针之间的相互转换。
同时,Go中的指针是不能进行运算的,包括unsafe.Pointer,但是uintptr是可以的,而uintptr和unsafe.Pointer可以互相转换,因此可以用来实现指针的运算。
接下来介绍一些变量及其在内存中的存储方法。
例如以下结构:
type T struct { F1 int32 F2 rune F3 int64 F4 *string } var t T
则变量t在内存中的布局可见下图:
其中变量t的指针&t指向的是t在内存中的起始地址。字段F1为int32类型,占4个字节;F2为rune,是int32的别名,也占4个字节;F3为int64,占用8个字节,最后是F4是*string类型,也是个指针,所以也占用8个字节。
在内存存储中,是没有变量类型信息的,其内部存放的内容到底如何处理,由变量的类型来决定。比如说上图中的F1字段,假设存储的内容为0x01,作为int32值解析时为1,作为bool值解析时就为true。也就是说,只要我知道内存地址,然后任意给定一个类型,就能得到这个地址里的内容作为这个类型处理时得到的值。
func main() { t := T{ F1: 1, F2: 2, } println(t.F1) b := *(*bool)(unsafe.Pointer(&t)) println(b) } // output: 1 true
既然任意类型都可以,那么我们可以以字节数组类型来解析,这样就可以拿到内存中的每个字节的数据了。
func main() { t := T{ F1: 1, F2: 2, } fmt.Println(t.F1) b := *(*bool)(unsafe.Pointer(&t)) fmt.Println(b) var arr [4]byte arr = *(*[4]byte)(unsafe.Pointer(&t)) fmt.Println(arr) } // output: 1 true [1 0 0 0]
由上面图中可以看到,F2字段的地址起始是在F1字段地址(也即变量t的起始地址)后偏移4个字节,那么可以通过指针运算的方式得到F2字段的地址及内部存储的内容:
func main() { t := T{ F1: 1, F2: 2, } // 此处只作为演示,用法有不妥的地方 // 具体正确用法请见unsafe.Pointer的源码注释 p2 := uintptr(unsafe.Pointer(&t)) + 4 fmt.Println(*(*[4]byte)(unsafe.Pointer(p2))) } // output: [2 0 0 0]
uintptr其实就是个整数,所以我们可以实现以下的方法来读取任意地址(当然地址是否合法另说)的4个字节的数据:
func readMemory(addr uintptr) [4]byte { return *(*[4]byte)(unsafe.Pointer(addr)) }
现在我们已经实现了读取任意地址的4个字节的数据,那有什么办法来读取任意字节的数据呢?Go中的数组结构长度是固定的,并且无法通过变量来指定长度。但是我们有切片(slice)啊!
都知道切片的底层结构是由数据地址、长度、容量三个字段组成的结构体,其与reflect.SliceHeader保持一致,那么我们只要将上面代码做如下改动即可:
func readMemory(addr uintptr, size int) []byte { return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ Data: addr, Len: size, Cap: size, })) }
这样,就实现了读取任意内存地址任意字节数据的功能。刚才只是读了单个字段、4个字节的数据,现在可以尝试直接读取前两个字段总共8个字节的数据了:
func main() { t := T{ F1: 1, F2: 2, } fmt.Println(readMemory(uintptr(unsafe.Pointer(&t)), 8)) } // output: [1 0 0 0 2 0 0 0]
完美!