| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- package aotinject
- /*
- #cgo CFLAGS: -I ../inject/include
- #cgo amd64 LDFLAGS: ${SRCDIR}/../inject/lib/libhotpatch_amd64.a
- #cgo arm64 LDFLAGS: ${SRCDIR}/../inject/lib/libhotpatch_arm64.a
- #include "hotpatch.h"
- #include <stdlib.h>
- */
- import "C"
- import (
- "bufio"
- "debug/dwarf"
- "debug/elf"
- "flag"
- "fmt"
- "io"
- "log"
- "os"
- "strconv"
- "strings"
- "syscall"
- "unsafe"
- )
- func InjectLib(pid int, libPath string) int {
- dll := C.CString(libPath) // 替换为实际的DLL路径
- fmt.Printf("dll: %s\n", libPath)
- rootfs := C.CString("")
- defer C.free(unsafe.Pointer(dll)) // 确保在使用完字符串后释放内存
- result := C.cw_inject_library(C.int(pid), C.int(0), dll, rootfs)
- fmt.Printf("Result JVM: %d\n", result)
- return int(result)
- }
- // readMemory 用于读取指定地址的内存数据
- func readMemory(pid int, address uint64, size uint64) ([]byte, error) {
- memFile := fmt.Sprintf("/proc/%d/mem", pid)
- file, err := os.Open(memFile)
- if err != nil {
- return nil, err
- }
- defer file.Close()
- data := make([]byte, size)
- _, err = file.ReadAt(data, int64(address))
- if err != nil {
- return nil, err
- }
- return data, nil
- }
- func getFunctionOffsetDBG(libPath, functionName string) (elf.Symbol, error) {
- dwarfFile, err := elf.Open(libPath)
- if err != nil {
- return elf.Symbol{}, fmt.Errorf("failed to open libPath %s: %v", libPath, err)
- }
- dwarfData, err := dwarfFile.DWARF()
- if err != nil {
- return elf.Symbol{}, fmt.Errorf("failed to read DWARF data: %v", err)
- }
- type uprobesDef struct {
- Name string
- Offset uint64
- EntAddress uint64
- RetAddress uint64
- }
- listEntry := make(map[dwarf.Offset]uprobesDef)
- SpecListEntry := []dwarf.Entry{}
- entryReader := dwarfData.Reader()
- for {
- entry, err := entryReader.Next()
- if err == io.EOF {
- // We've reached the end of DWARF entries
- break
- }
- if err != nil {
- log.Println("Error reading entry: %v", err)
- }
- if entry == nil {
- log.Println("Warning: a nil entry was returned with no error")
- break
- }
- // fmt.Printf("-----entry address: %v, %v, %v, %v, %v\n", entry, entry.Val(dwarf.AttrName), entry.Tag, entry.Val(dwarf.AttrLowpc), entry.Val(dwarf.AttrHighpc))
- if entry.Tag == dwarf.TagSubprogram {
- // fmt.Printf("entry address: %x, %d\n", entry.Offset, entry.Children)
- funName, ok := entry.Val(dwarf.AttrName).(string)
- if !ok {
- continue
- }
- if functionName == funName {
- entAddress, ok := entry.Val(dwarf.AttrLowpc).(uint64)
- if !ok {
- entAddress = 0
- }
- retAddress, ok := entry.Val(dwarf.AttrHighpc).(uint64)
- if !ok {
- retAddress = 0
- }
- fmt.Printf("Function %s address: %x, %x, %x\n", funName, entAddress, entry.Offset, retAddress)
- uprobes := uprobesDef{
- Name: funName,
- Offset: uint64(entry.Offset),
- EntAddress: entAddress,
- RetAddress: retAddress,
- }
- listEntry[entry.Offset] = uprobes
- }
- specAddr, _ := entry.Val(dwarf.AttrSpecification).(dwarf.Offset)
- lowpc := entry.Val(dwarf.AttrLowpc)
- if lowpc != nil && specAddr > 0 && lowpc.(uint64) > 0 {
- SpecListEntry = append(SpecListEntry, *entry)
- }
- }
- }
- for _, v := range SpecListEntry {
- specAddr, _ := v.Val(dwarf.AttrSpecification).(dwarf.Offset)
- // fmt.Printf("SpecListEntrySpecListEntrySpecListEntry Attach Function: %x\n", specAddr)
- _, ok := listEntry[specAddr]
- if ok {
- vv := listEntry[specAddr]
- entAddr := v.Val(dwarf.AttrLowpc)
- if entAddr != nil {
- vv.EntAddress = entAddr.(uint64)
- }
- retAddr := v.Val(dwarf.AttrHighpc)
- if retAddr != nil {
- switch retAddr.(type) {
- case uint64:
- vv.RetAddress = uint64(retAddr.(uint64))
- case int64:
- vv.RetAddress = uint64(retAddr.(int64))
- default:
- fmt.Println("Unknown type")
- }
- }
- listEntry[specAddr] = vv
- }
- }
- for _, v := range listEntry {
- sSize := v.RetAddress
- if v.RetAddress > v.EntAddress {
- sSize = v.RetAddress - v.EntAddress
- }
- symbol := elf.Symbol{
- Name: v.Name,
- Value: v.EntAddress,
- Size: 180,
- }
- fmt.Printf("Need Attach Function %s address: %x, %x, %d\n", v.Name, v.EntAddress, v.RetAddress, sSize)
- return symbol, nil
- }
- return elf.Symbol{}, fmt.Errorf("function %s not found", functionName)
- }
- func getFunctionOffset(libPath, functionName string) (elf.Symbol, error) {
- elfFile, err := elf.Open(libPath)
- if err != nil {
- return elf.Symbol{}, fmt.Errorf("failed to open ELF file: %v", err)
- }
- defer elfFile.Close()
- // 优先从 Symbols 中查找函数的地址,如果找不到则从 DynamicSymbols 中查找
- symbols, err := elfFile.Symbols()
- if err != nil {
- fmt.Printf("Can Not find Symbols: %s, %s, %v, can find from DynamicSymbols\n", libPath, functionName, err)
- symbols, err = elfFile.DynamicSymbols()
- if err != nil {
- return elf.Symbol{}, fmt.Errorf("failed to read dynamic symbols: %v", err)
- }
- }
- for _, sym := range symbols {
- if sym.Name == functionName {
- return sym, nil
- }
- }
- return elf.Symbol{}, fmt.Errorf("function %s not found from Symbols", functionName)
- }
- // getProcessMapsInfo reads the first line of /proc/<pid>/maps file and returns the memory map as a MemoryMap struct
- // If a library name is provided, it returns the start address of that library
- func getProcessMapsInfo(pid int, libraryName ...string) (*ProcessMapsInfo, error) {
- file, err := os.Open(fmt.Sprintf("/proc/%d/maps", pid))
- if err != nil {
- return nil, err
- }
- defer file.Close()
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := scanner.Text()
- fields := strings.Fields(line)
- addresses := strings.Split(fields[0], "-")
- if len(addresses) != 2 {
- return nil, fmt.Errorf("unexpected format in /proc/%d/maps", pid)
- }
- start, err := strconv.ParseUint(addresses[0], 16, 64)
- if err != nil {
- return nil, err
- }
- end, err := strconv.ParseUint(addresses[1], 16, 64)
- if err != nil {
- return nil, err
- }
- if len(libraryName) == 0 || (len(fields) > 5 && strings.Contains(fields[5], libraryName[0])) {
- return &ProcessMapsInfo{
- Start: start,
- End: end,
- Path: fields[5],
- }, nil
- }
- }
- if err := scanner.Err(); err != nil {
- return nil, err
- }
- return nil, fmt.Errorf("specified library or process start address not found in /proc/%d/maps", pid)
- }
- func GetProcessFunctionInfo(pid int, libPath string, functionName string, libraryName ...string) (*ProcessFunctionInfo, error) {
- processMapsInfo, _ := getProcessMapsInfo(pid, libraryName...)
- baseAddress := uint64(processMapsInfo.Start)
- // functionName 是以 @plt 结尾的函数名,则调用 getFunctionOffsetPLT 获取函数的地址,并且去掉 @plt 后缀
- if strings.HasSuffix(functionName, "@plt") {
- functionName = functionName[:len(functionName)-4]
- sendFunctionNew, _ := getFunctionOffsetPLT(pid, libPath, functionName)
- return &ProcessFunctionInfo{
- Name: functionName,
- Offset: sendFunctionNew.Value,
- Start: baseAddress + sendFunctionNew.Value,
- Size: sendFunctionNew.Size,
- }, nil
- }
- // 获取函数的偏移量,有限直接从 ELF 文件中获取,如果失败则从 DWARF 文件中获取
- functionSym, err := getFunctionOffset(libPath, functionName)
- if err != nil {
- fmt.Printf("Error getting function offset: %v, now to find dbg %s\n", err, libPath+".dbg")
- functionSym, err = getFunctionOffsetDBG(libPath+".dbg", functionName)
- if err != nil {
- fmt.Printf("Error getting function offset from DWARF file: %v\n", err)
- return nil, err
- }
- }
- fmt.Printf("get ProcessFunctionInfo: 0x%x, 0x%x\n", baseAddress, functionSym.Value)
- funRelAddr := functionSym.Value
- if functionSym.Value < baseAddress {
- funRelAddr += baseAddress
- }
- return &ProcessFunctionInfo{
- Name: functionSym.Name,
- Offset: functionSym.Value,
- Start: funRelAddr,
- Size: functionSym.Size,
- }, nil
- }
- func AotInject(aotInjector AOTInjector) (int, error) {
- isInject := false
- // lib 注入
- cwLibraryInfo, err := getProcessMapsInfo(aotInjector.PID, aotInjector.CWLibName)
- if err != nil {
- fmt.Printf("Not Find Lib %s: Now to InjectLib: %v\n", aotInjector.CWLibName, err)
- InjectLib(aotInjector.PID, aotInjector.CWLibPath)
- cwLibraryInfo, err = getProcessMapsInfo(aotInjector.PID, aotInjector.CWLibName)
- if err != nil {
- fmt.Printf("InjectLib %s Err: %v\n", aotInjector.CWLibName, err)
- return 0, fmt.Errorf("InjectLib %s Err: %v", aotInjector.CWLibName, err)
- }
- } else {
- isInject = true
- }
- fmt.Printf("find %s Lib Start: %x, End: %x, Path: %s\n", aotInjector.CWLibName, cwLibraryInfo.Start, cwLibraryInfo.End, cwLibraryInfo.Path)
- // 查找 cwFunctionName 函数的偏移量
- cwSym, _ := GetProcessFunctionInfo(aotInjector.PID, cwLibraryInfo.Path, aotInjector.CWFunctionName, aotInjector.CWLibName)
- // 打印 cwFunctionName 函数的偏移量
- fmt.Printf("%s: addr %x, size %d\n", aotInjector.CWFunctionName, cwSym.Start, cwSym.Size)
- if isInject == true {
- fmt.Printf("cwLibrary is already Injected, to find hookOffset\n")
- hookOffset, err := findNopFunctionHookOffset(aotInjector.PID, cwSym)
- if err != nil {
- fmt.Printf("findNopFunctionHookOffset Err: %v Need Inject\n", err)
- } else {
- fmt.Printf("findNopFunctionHookOffset: %d[%d]\n", hookOffset, hookOffset+nopEntryOffset)
- return hookOffset + nopEntryOffset, nil
- }
- }
- // 获取socketWrite0 和 NET_Send 函数的偏移量
- jvmInjectorFunction, err := GetProcessFunctionInfo(aotInjector.PID, aotInjector.InjectLibPath, aotInjector.InjectFunctionName)
- if err != nil {
- fmt.Printf("Error getting function %s offset: %v\n", aotInjector.InjectFunctionName, err)
- return 0, fmt.Errorf("error getting function %s offset: %v", aotInjector.InjectFunctionName, err)
- }
- sendFunction, err := GetProcessFunctionInfo(aotInjector.PID, aotInjector.SendFunctionLibPath, aotInjector.SendFunctionName)
- if err != nil {
- fmt.Printf("Error getting function %s offset: %v\n", aotInjector.SendFunctionName, err)
- return 0, fmt.Errorf("error getting function %s offset: %v", aotInjector.SendFunctionName, err)
- }
- fmt.Printf("find %s, addr: %x, size:%d\n", aotInjector.InjectFunctionName, jvmInjectorFunction.Start, jvmInjectorFunction.Size)
- fmt.Printf("find %s, addr: %x, size:%d\n", aotInjector.SendFunctionName, sendFunction.Start, sendFunction.Size)
- // 读取 socketWrite0 函数的内存数据并解析 NET_Send 在 socketWrite0 中的调用位置
- code, err := readMemory(aotInjector.PID, jvmInjectorFunction.Start, jvmInjectorFunction.Size)
- if err != nil {
- fmt.Printf("Error reading jvmInjectorFunction memory: %v\n", err)
- return 0, fmt.Errorf("error reading jvmInjectorFunction memory: %v", err)
- }
- // 打印 socketWrite0 函数的内存数据
- fmt.Printf("socketWrite0 code: %v\n", code)
- SendFunctionAbsOffset, SendFunctionRelOffset, err := findSendFunctionNameAddr(jvmInjectorFunction.Start, code, aotInjector.SendFunctionName, sendFunction.Start)
- if err != nil {
- fmt.Printf("findSendFunctionNameAddr Err: %v\n", err)
- return 0, fmt.Errorf("findSendFunctionNameAddr Err: %v", err)
- }
- fmt.Printf("find %s : SendFunctionAbsOffset:%x, SendFunctionRelOffset: %d\n", aotInjector.SendFunctionName, SendFunctionAbsOffset, SendFunctionRelOffset)
- // 尝试获取 NET_Send 附近的数据凑够 17 个字节,用于长跳转到 libmylib 中
- instMemStruct, err := findMemForLongJump(jvmInjectorFunction.Start, code, aotInjector.SendFunctionName, SendFunctionAbsOffset)
- if err != nil {
- fmt.Printf("findMemForLongJump Err: %v\n", err)
- return 0, fmt.Errorf("findMemForLongJump Err: %v", err)
- }
- // 打印 instMemStruct 结构体的内容
- fmt.Printf("instMemStruct: %v\n", instMemStruct)
- // 生成长跳指令,跳转到 libmylib 中的 cwFunctionName 函数的+12地址
- jumpCode := generateLongJumpCode(cwSym.Start+nopEntryOffset, len(instMemStruct.CodeArray), true)
- // 使用 parseAndPrintInstructions打印 jumpCode 的内容
- fmt.Printf("jumpCode: %v\n", jumpCode)
- parseAndPrintInstructions(jumpCode)
- // 生成新的指令,用于替换原来的指令,一个参数是原来的指令 instCodeArray,另一个参数是跳转回去的地址,第三个参数是原始 Send 函数的地址,第四个参数是在哪个指令前插入自定义指令
- newCode, hookOffset := generateNewCode(instMemStruct.InstCodeArray, instMemStruct.InstStartAddr+jumpBackAddrOffset, sendFunction.Start, instMemStruct.InsertIndex)
- if hookOffset == 0 {
- fmt.Printf("generateNewCode Err: %v\n", instMemStruct)
- return 0, fmt.Errorf("generateNewCode Err: %v", instMemStruct)
- }
- // 打印 hookIndex
- fmt.Printf("ebpf should hookIndex: %d[%d]\n", hookOffset, nopEntryOffset+hookOffset)
- // 使用 parseAndPrintInstructions 打印 newCode 的内容
- fmt.Printf("newCode: %v\n", newCode)
- parseAndPrintInstructions(newCode)
- // 使用 ptrace attach 目标进程
- fmt.Printf("Attach Process: %d\n", aotInjector.PID)
- if err := syscall.PtraceAttach(aotInjector.PID); err != nil {
- fmt.Printf("PtraceAttach Err: %v\n", err)
- return 0, fmt.Errorf("ptrace attach: %v", err)
- }
- // 等待目标进程停止
- if _, err := syscall.Wait4(aotInjector.PID, nil, 0, nil); err != nil {
- fmt.Printf("wait4: %v", err)
- if err = syscall.PtraceDetach(aotInjector.PID); err != nil {
- fmt.Printf("ptrace DETACH: %v", err)
- }
- return 0, fmt.Errorf("wait4: %v", err)
- }
- fmt.Printf("Ptrace 将newCode使用ptrace写入到目标进程的内存中: %x\n", cwSym.Start+nopEntryOffset)
- // 将newCode使用ptrace写入到目标进程的的空白内存中,地址为 cwSym.Start+12
- _, err = syscall.PtracePokeData(aotInjector.PID, uintptr(cwSym.Start+nopEntryOffset), newCode)
- if err != nil {
- fmt.Printf("PtracePokeData Err: %v\n", err)
- }
- fmt.Printf("Ptrace 将jumpCode使用ptrace写入到目标进程的内存中: %x\n", instMemStruct.InstStartAddr)
- // 将jumpCode使用ptrace写入到目标进程的内存中
- _, err = syscall.PtracePokeData(aotInjector.PID, uintptr(instMemStruct.InstStartAddr), jumpCode)
- if err != nil {
- fmt.Printf("PtracePokeData Err: %v\n", err)
- }
- fmt.Printf("Ptrace DETACH: %d\n", aotInjector.PID)
- // 恢复执行
- if err = syscall.PtraceDetach(aotInjector.PID); err != nil {
- fmt.Printf("ptrace DETACH: %v", err)
- }
- return hookOffset + nopEntryOffset, nil
- }
- type AOTInjector struct {
- PID int
- SendFunctionLibPath string
- SendFunctionName string
- InjectLibPath string
- InjectFunctionName string
- CWLibName string
- CWFunctionName string
- CWLibPath string
- }
- func main() {
- flag.StringVar(&PID, "p", "", "PID")
- flag.Parse()
- pidStr := PID // 替换为目标进程的 PID
- pid, err := strconv.Atoi(pidStr)
- if err != nil {
- log.Fatalf("Invalid PID: %v", err)
- }
- // NETCoreAOTInject
- // netCoreAotInjector := AOTInjector{
- // PID: pid,
- // SendFunctionLibPath: "/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT",
- // SendFunctionName: "send@plt",
- // InjectLibPath: "/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT",
- // InjectFunctionName: "SystemNative_Send",
- // CWLibName: "libmylib.so",
- // CWFunctionName: "asmnop",
- // CWLibPath: "/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT",
- // }
- // JavaAOTInject
- javaAotInjector := AOTInjector{
- PID: pid,
- SendFunctionLibPath: "/data/roger/ebpfdemo/graalve_demo/SimpleHttpServer",
- SendFunctionName: "NET_Send",
- InjectLibPath: "/data/roger/ebpfdemo/graalve_demo/SimpleHttpServer",
- InjectFunctionName: "Java_java_net_SocketOutputStream_socketWrite0",
- CWLibName: "libmylib.so",
- CWFunctionName: "asmnop",
- CWLibPath: "/data/roger/ebpfdemo/mylib/libmylib.so",
- }
- hookOffset, err := AotInject(javaAotInjector)
- if err != nil {
- fmt.Printf("AotInject Err: %v\n", err)
- }
- fmt.Printf("AotInject result hookOffset: %d\n", hookOffset)
- }
|