...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/ptrace/ptrace_linux_amd64.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/ptrace

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  //go:build cgo
    16  
    17  package ptrace
    18  
    19  import (
    20  	"encoding/binary"
    21  	"syscall"
    22  
    23  	"github.com/pkg/errors"
    24  )
    25  
    26  var endian = binary.LittleEndian
    27  
    28  const syscallInstrSize = 2
    29  
    30  const nrProcessVMReadv = 310
    31  const nrProcessVMWritev = 311
    32  
    33  func getIp(regs *syscall.PtraceRegs) uintptr {
    34  	return uintptr(regs.Rip)
    35  }
    36  
    37  func getRegs(pid int, regsout *syscall.PtraceRegs) error {
    38  	err := syscall.PtraceGetRegs(pid, regsout)
    39  	if err != nil {
    40  		return errors.Wrapf(err, "get registers of process %d", pid)
    41  	}
    42  
    43  	return nil
    44  }
    45  
    46  func setRegs(pid int, regs *syscall.PtraceRegs) error {
    47  	err := syscall.PtraceSetRegs(pid, regs)
    48  	if err != nil {
    49  		return errors.Wrapf(err, "set registers of process %d", pid)
    50  	}
    51  
    52  	return nil
    53  }
    54  
    55  // Syscall runs a syscall at main thread of process
    56  func (p *TracedProgram) Syscall(number uint64, args ...uint64) (uint64, error) {
    57  	// save the original registers and the current instructions
    58  	err := p.Protect()
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  
    63  	var regs syscall.PtraceRegs
    64  
    65  	err = getRegs(p.pid, &regs)
    66  	if err != nil {
    67  		return 0, err
    68  	}
    69  	// set the registers according to the syscall convention. Learn more about
    70  	// it in `man 2 syscall`. In x86_64 the syscall nr is stored in rax
    71  	// register, and the arguments are stored in rdi, rsi, rdx, r10, r8, r9 in
    72  	// order
    73  	regs.Rax = number
    74  	for index, arg := range args {
    75  		// All these registers are hard coded for x86 platform
    76  		if index == 0 {
    77  			regs.Rdi = arg
    78  		} else if index == 1 {
    79  			regs.Rsi = arg
    80  		} else if index == 2 {
    81  			regs.Rdx = arg
    82  		} else if index == 3 {
    83  			regs.R10 = arg
    84  		} else if index == 4 {
    85  			regs.R8 = arg
    86  		} else if index == 5 {
    87  			regs.R9 = arg
    88  		} else {
    89  			return 0, errors.New("too many arguments for a syscall")
    90  		}
    91  	}
    92  	err = setRegs(p.pid, &regs)
    93  	if err != nil {
    94  		return 0, err
    95  	}
    96  
    97  	instruction := make([]byte, syscallInstrSize)
    98  	ip := getIp(p.backupRegs)
    99  
   100  	// set the current instruction (the ip register points to) to the `syscall`
   101  	// instruction. In x86_64, the `syscall` instruction is 0x050f.
   102  	binary.LittleEndian.PutUint16(instruction, 0x050f)
   103  	_, err = syscall.PtracePokeData(p.pid, ip, instruction)
   104  	if err != nil {
   105  		return 0, errors.Wrapf(err, "writing data %v to %x", instruction, ip)
   106  	}
   107  
   108  	// run one instruction, and stop
   109  	err = p.Step()
   110  	if err != nil {
   111  		return 0, err
   112  	}
   113  
   114  	// read registers, the return value of syscall is stored inside rax register
   115  	err = getRegs(p.pid, &regs)
   116  	if err != nil {
   117  		return 0, err
   118  	}
   119  
   120  	// restore the state saved at beginning.
   121  	return regs.Rax, p.Restore()
   122  }
   123  
   124  // JumpToFakeFunc writes jmp instruction to jump to fake function
   125  func (p *TracedProgram) JumpToFakeFunc(originAddr uint64, targetAddr uint64) error {
   126  	instructions := make([]byte, 16)
   127  
   128  	// mov rax, targetAddr;
   129  	// jmp rax ;
   130  	instructions[0] = 0x48
   131  	instructions[1] = 0xb8
   132  	endian.PutUint64(instructions[2:10], targetAddr)
   133  	instructions[10] = 0xff
   134  	instructions[11] = 0xe0
   135  
   136  	return p.PtraceWriteSlice(originAddr, instructions)
   137  }
   138