Skip to content

Commit

Permalink
proc.(*Thread).GetG: reading TLS memory directly for g address instea…
Browse files Browse the repository at this point in the history
…d of modifying the executable code
  • Loading branch information
aarzilli committed Jul 24, 2015
1 parent bd13e32 commit 941b295
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 115 deletions.
26 changes: 7 additions & 19 deletions proc/arch.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type Arch interface {
PtrSize() int
BreakpointInstruction() []byte
BreakpointSize() int
CurgInstructions() []byte
GStructOffset() uint64
HardwareBreakpointUsage() []bool
SetHardwareBreakpointUsage(int, bool)
}
Expand All @@ -16,7 +16,7 @@ type AMD64 struct {
ptrSize int
breakInstruction []byte
breakInstructionLen int
curgInstructions []byte
gStructOffset uint64
hardwareBreakpointUsage []bool
}

Expand All @@ -32,28 +32,16 @@ func AMD64Arch() *AMD64 {
}

func (a *AMD64) SetCurGInstructions(ver GoVersion, isextld bool) {
var curg []byte

switch runtime.GOOS {
case "darwin":
curg = []byte{
0x65, 0x48, 0x8b, 0x0C, 0x25, 0xA0, 0x08, // mov %gs:0x8a0,%rcx
0x0, 0x0,
}
a.gStructOffset = 0x8a0
case "linux":
if isextld || ver.After(GoVersion{1, 5, 0}) {
curg = []byte{
0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf8, 0xff, 0xff, 0xff, // mov %fs:0xfffffffffffffff8,%rcx
}
a.gStructOffset = 0xfffffffffffffff8
} else {
curg = []byte{
0x64, 0x48, 0x8b, 0x0c, 0x25, 0xf0, 0xff, 0xff, 0xff, // mov %fs:0xfffffffffffffff0,%rcx
}
a.gStructOffset = 0xfffffffffffffff0
}
}
curg = append(curg, a.breakInstruction...)

a.curgInstructions = curg
}

func (a *AMD64) PtrSize() int {
Expand All @@ -68,8 +56,8 @@ func (a *AMD64) BreakpointSize() int {
return a.breakInstructionLen
}

func (a *AMD64) CurgInstructions() []byte {
return a.curgInstructions
func (a *AMD64) GStructOffset() uint64 {
return a.gStructOffset
}

func (a *AMD64) HardwareBreakpointUsage() []bool {
Expand Down
1 change: 1 addition & 0 deletions proc/registers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Registers interface {
PC() uint64
SP() uint64
CX() uint64
TLS() uint64
SetPC(*Thread, uint64) error
String() string
}
Expand Down
108 changes: 66 additions & 42 deletions proc/registers_darwin_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,28 @@ import (
)

type Regs struct {
rax uint64
rbx uint64
rcx uint64
rdx uint64
rdi uint64
rsi uint64
rbp uint64
rsp uint64
r8 uint64
r9 uint64
r10 uint64
r11 uint64
r12 uint64
r13 uint64
r14 uint64
r15 uint64
rip uint64
rflags uint64
cs uint64
fs uint64
gs uint64
rax uint64
rbx uint64
rcx uint64
rdx uint64
rdi uint64
rsi uint64
rbp uint64
rsp uint64
r8 uint64
r9 uint64
r10 uint64
r11 uint64
r12 uint64
r13 uint64
r14 uint64
r15 uint64
rip uint64
rflags uint64
cs uint64
fs uint64
gs uint64
gs_base uint64
}

func (r *Regs) String() string {
Expand Down Expand Up @@ -58,6 +59,7 @@ func (r *Regs) String() string {
{"Cs", r.cs},
{"Fs", r.fs},
{"Gs", r.gs},
{"Gs_base", r.gs_base},
}
for _, reg := range regs {
fmt.Fprintf(&buf, "%s = 0x%x\n", reg.k, reg.v)
Expand All @@ -77,6 +79,10 @@ func (r *Regs) CX() uint64 {
return r.rcx
}

func (r *Regs) TLS() uint64 {
return r.gs_base
}

func (r *Regs) SetPC(thread *Thread, pc uint64) error {
kret := C.set_pc(thread.os.thread_act, C.uint64_t(pc))
if kret != C.KERN_SUCCESS {
Expand All @@ -87,32 +93,50 @@ func (r *Regs) SetPC(thread *Thread, pc uint64) error {

func registers(thread *Thread) (Registers, error) {
var state C.x86_thread_state64_t
var identity C.thread_identifier_info_data_t
kret := C.get_registers(C.mach_port_name_t(thread.os.thread_act), &state)
if kret != C.KERN_SUCCESS {
return nil, fmt.Errorf("could not get registers")
}
kret = C.get_identity(C.mach_port_name_t(thread.os.thread_act), &identity)
if kret != C.KERN_SUCCESS {
return nil, fmt.Errorf("could not get thread identity informations")
}
/*
thread_identifier_info::thread_handle contains the base of the
thread-specific data area, which on x86 and x86_64 is the thread’s base
address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
thread_info_internal() gets the value from
machine_thread::cthread_self, which is the same value used to set the
%gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
act_machine_switch_pcb().
--
comment copied from chromium's crashpad
https://chromium.googlesource.com/crashpad/crashpad/+/master/snapshot/mac/process_reader.cc
*/
regs := &Regs{
rax: uint64(state.__rax),
rbx: uint64(state.__rbx),
rcx: uint64(state.__rcx),
rdx: uint64(state.__rdx),
rdi: uint64(state.__rdi),
rsi: uint64(state.__rsi),
rbp: uint64(state.__rbp),
rsp: uint64(state.__rsp),
r8: uint64(state.__r8),
r9: uint64(state.__r9),
r10: uint64(state.__r10),
r11: uint64(state.__r11),
r12: uint64(state.__r12),
r13: uint64(state.__r13),
r14: uint64(state.__r14),
r15: uint64(state.__r15),
rip: uint64(state.__rip),
rflags: uint64(state.__rflags),
cs: uint64(state.__cs),
fs: uint64(state.__fs),
gs: uint64(state.__gs),
rax: uint64(state.__rax),
rbx: uint64(state.__rbx),
rcx: uint64(state.__rcx),
rdx: uint64(state.__rdx),
rdi: uint64(state.__rdi),
rsi: uint64(state.__rsi),
rbp: uint64(state.__rbp),
rsp: uint64(state.__rsp),
r8: uint64(state.__r8),
r9: uint64(state.__r9),
r10: uint64(state.__r10),
r11: uint64(state.__r11),
r12: uint64(state.__r12),
r13: uint64(state.__r13),
r14: uint64(state.__r14),
r15: uint64(state.__r15),
rip: uint64(state.__rip),
rflags: uint64(state.__rflags),
cs: uint64(state.__cs),
fs: uint64(state.__fs),
gs: uint64(state.__gs),
gs_base: uint64(identity.thread_handle),
}
return regs, nil
}
Expand Down
4 changes: 4 additions & 0 deletions proc/registers_linux_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func (r *Regs) CX() uint64 {
return r.regs.Rcx
}

func (r *Regs) TLS() uint64 {
return r.regs.Fs_base
}

func (r *Regs) SetPC(thread *Thread, pc uint64) (err error) {
r.regs.SetPC(pc)
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.Id, r.regs) })
Expand Down
64 changes: 11 additions & 53 deletions proc/threads.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package proc

import (
"debug/gosym"
"encoding/binary"
"fmt"
"path/filepath"

Expand Down Expand Up @@ -257,65 +258,22 @@ func (thread *Thread) SetPC(pc uint64) error {
// opposed to the runtime version. This has the consequence of not setting M.id for
// any thread, regardless of OS.
//
// In order to get around all this craziness, we write the instructions to retrieve the G
// structure running on this thread (which is stored in thread local memory) into the
// current instruction stream. The instructions are obviously arch/os dependant, as they
// vary on how thread local storage is implemented, which MMU register is used and
// what the offset into thread local storage is.
// In order to get around all this craziness, we read the address of the G structure for
// the current thread from the thread local storage area.
func (thread *Thread) GetG() (g *G, err error) {
var pcInt uint64
pcInt, err = thread.PC()
regs, err := thread.Registers()
if err != nil {
return
}
pc := uintptr(pcInt)
// Read original instructions.
originalInstructions := make([]byte, len(thread.dbp.arch.CurgInstructions()))
if _, err = readMemory(thread, pc, originalInstructions); err != nil {
return
}
// Write new instructions.
if _, err = writeMemory(thread, pc, thread.dbp.arch.CurgInstructions()); err != nil {
return
}
// We're going to be intentionally modifying the registers
// once we execute the code we inject into the instruction stream,
// so save them off here so we can restore them later.
if _, err = thread.saveRegisters(); err != nil {
return
}
// Ensure original instructions and PC are both restored.
defer func() {
// Do not shadow previous error, if there was one.
originalErr := err
// Restore the original instructions and register contents.
if _, err = writeMemory(thread, pc, originalInstructions); err != nil {
return
}
if err = thread.restoreRegisters(); err != nil {
return
}
err = originalErr
return
}()
// Execute new instructions.
if err = thread.resume(); err != nil {
return
}
// Set the halt flag so that trapWait will ignore the fact that
// we hit a breakpoint that isn't captured in our list of
// known breakpoints.
thread.dbp.halt = true
defer func(dbp *Process) { dbp.halt = false }(thread.dbp)
if _, err = thread.dbp.trapWait(-1); err != nil {
return
return nil, err
}
// Grab *G from RCX.
regs, err := thread.Registers()

gaddrbs := make([]byte, 8)
_, err = readMemory(thread, uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), gaddrbs)
if err != nil {
return nil, err
}
g, err = parseG(thread, regs.CX(), false)
gaddr := binary.LittleEndian.Uint64(gaddrbs)

g, err = parseG(thread, gaddr, false)
if err == nil {
g.thread = thread
}
Expand Down
7 changes: 6 additions & 1 deletion proc/threads_darwin.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,16 @@ kern_return_t
get_registers(mach_port_name_t task, x86_thread_state64_t *state) {
kern_return_t kret;
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;

// TODO(dp) - possible memory leak - vm_deallocate state
return thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)state, &stateCount);
}

kern_return_t
get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) {
mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT;
return thread_info(task, THREAD_IDENTITY_INFO, (thread_info_t)idinfo, &idinfoCount);
}

kern_return_t
set_registers(mach_port_name_t task, x86_thread_state64_t *state) {
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
Expand Down

0 comments on commit 941b295

Please sign in to comment.