63

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

 4 years ago
source link: https://www.jianshu.com/p/7b3638b47845?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
0.2642019.08.22 20:45:47字数 483阅读 2,338

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

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类似,骗过编译器。直接访问变量


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK