Golang 隐藏技能 -- 访问私有成员

警告一下!以下代码均不是常规操作,且存在各种潜在不可控的风险。在项目中应用有可能被同事打死,慎用!!!

1.调用其他包中公有结构的私有成员变量

如果需要引用某个包中公有结构体的私有变量,而这个变量又没有提供对应的访问方法。那么如何绕过 “小写不公开” 这个限制呢?简单介绍一种方法直接通过变量地址访问变量:

package other1

import "fmt"

type TestPointer struct {
    A int
    b int    // 私有变量
}

func (T *TestPointer) OouPut() {
    fmt.Println("TestPointer OouPut:", T.A, T.b)
}
package main

import (
    "fmt"
    "test/test4/other1"
    "unsafe"
)

func main() {
    T := other1.TestPointer{A:1}
    pb := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&T)) + 8))
    /*
    Tmp := uintptr(unsafe.Pointer(&T)) + 8)
    pb := (*int)(unsafe.Pointer(Tmp)
    千万不能出现这种用临时变量中转一下的情况。因为GC可能因为优化内存碎片的原因移动了这个对象。只保留了指针的地址是没有意义的。
    */
    *pb = 2
    T.OouPut()    //1 2
}

用unsafe包中的unsafe.Pointer获取到结构体对象的首地址,然后加上想访问的私有变量的偏移地址就是私有变量的地址。关于成员变量偏移量的问题请参阅 内存对齐

2.调用其他包的私有func

go提供了一个编译指令,绕过编译器检查。直接访问func的实现

//go:linkname

package other1

import "fmt"

func privateFunc() {
    fmt.Println("this is private func")
}
package main

import (
    _ "test/test4/other1"
    _ "unsafe"
)
// call private func
//go:linkname private_func test/test4/other1.privateFunc
func private_func()

func main() {
    private_func() // this is private func
}

关于编译指令可以参阅 编译指令Command compile

上面代码需要在调用者(这里是main.go)同目录添加一个 .s汇编文件,骗过编译器。让编译器认为是实现是在 .s汇编文件中,从而跳过检查

3. 调用其他包的公有结构的私有方法

package other1
import "fmt"
type PublicStruct struct {
    I int
    b int
}
func (p *PublicStruct) f(a int) {
    println("PublicStruct f()", p.I, p.b, a)
}
package main
import (
    "test/test4/other1"
    _ "unsafe"
)
// 调用其他包的公有结构的私有func
//go:linkname public_struc_private_func test/test4/other1.(*PublicStruct).f
func public_struc_private_func(p *other1.PublicStruct, a int)

func main() {
    // 先构造一个other1.PublicStruct
    p := &other1.PublicStruct{I:1}
    public_struc_private_func(p, 100)   // PublicStruct f() 1 0 100
}

和上面的类似用linkname指令骗过编译器。这里声明了一个指针接收者的func public_struc_private_func

第一个参数是对应对象的指针,第二个参数开始是对应func需要的参数。

其实这就是指针接收者func原本的实现方式(即 本质上是一个普通的函数,只是隐式传递了对象的指针)

4. 调用其他包的私有全局变量

package other1
var private_m = map[int]string {
    1:"a",
}
import (
    "fmt"
    _ "test/test4/other1"
    _ "unsafe"
) // 调用其他包的私有全局变量
//go:linkname private_member test/test4/other1.private_m
var private_member map[int]string

func main() {
    fmt.Println(private_member[1])  // a
    private_member[2] = "b"
    for k, v := range private_member {
        fmt.Println(k, v)       // 1 a; 2 b
    }
}

和上面的linkname类似,骗过编译器。直接访问变量

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章