-
Notifications
You must be signed in to change notification settings - Fork 1
/
break-oo-privacy-1.go
82 lines (71 loc) · 2.8 KB
/
break-oo-privacy-1.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package main
import (
"bufio"
"errors"
"fmt"
"os"
"reflect"
"unsafe"
)
type foobar struct {
Exported int // In Go identifiers that are capitalized are exported,
unexported int // while lowercase identifiers are not.
}
func main() {
obj := foobar{12, 42}
fmt.Println("obj:", obj)
examineAndModify(&obj)
fmt.Println("obj:", obj)
anotherExample()
}
// For simplicity this skips several checks. It assumes the thing in the
// interface is a pointer without checking (v.Kind()==reflect.Ptr),
// it then assumes it is a structure type with two int fields
// (v.Kind()==reflect.Struct, f.Type()==reflect.TypeOf(int(0))).
func examineAndModify(any interface{}) {
v := reflect.ValueOf(any) // get a reflect.Value
v = v.Elem() // dereference the pointer
fmt.Println(" v:", v, "=", v.Interface())
t := v.Type()
// Loop through the struct fields
fmt.Printf(" %3s %-10s %-4s %s\n", "Idx", "Name", "Type", "CanSet")
for i := 0; i < v.NumField(); i++ {
f := v.Field(i) // reflect.Value of the field
fmt.Printf(" %2d: %-10s %-4s %t\n", i,
t.Field(i).Name, f.Type(), f.CanSet())
}
// "Exported", field 0, has CanSet==true so we can do:
v.Field(0).SetInt(16)
// "unexported", field 1, has CanSet==false so the following
// would fail at run-time with:
// panic: reflect: reflect.Value.SetInt using value obtained using unexported field
//v.Field(1).SetInt(43)
// However, we can bypass this restriction with the unsafe
// package once we know what type it is (so we can use the
// correct pointer type, here *int):
vp := v.Field(1).Addr() // Take the fields's address
uip := vp.Pointer() // … get an int value of the address
up := unsafe.Pointer(uip) // … convert it "unsafely"
p := (*int)(up) // … and end up with what we want/need
fmt.Printf(" vp has type %-14T = %v\n", vp, vp)
fmt.Printf(" uip has type %-14T = %#0x\n", uip, uip)
fmt.Printf(" up has type %-14T = %#0x\n", up, up)
fmt.Printf(" p has type %-14T = %v pointing at %v\n", p, p, *p)
*p = 43 // effectively obj.unexported = 43
// or an incr all on one ulgy line:
*(*int)(unsafe.Pointer(v.Field(1).Addr().Pointer()))++
}
// This time we'll use an external package to demonstrate that it's not
// restricted to things defined locally. We'll mess with bufio.Reader's
// interal workings by happening to know they have a non-exported
// "err error" field. Of course future versions of Go may not have this
// field or use it in the same way :).
func anotherExample() {
r := bufio.NewReader(os.Stdin)
// Do the dirty stuff in one ugly and unsafe statement:
errp := (*error)(unsafe.Pointer(
reflect.ValueOf(r).Elem().FieldByName("err").Addr().Pointer()))
*errp = errors.New("unsafely injected error value into bufio inner workings")
_, err := r.ReadByte()
fmt.Println("bufio.ReadByte returned error:", err)
}