Prechádzať zdrojové kódy

Fixed #TASK_QT-9810 commit inject_asm_code for arm

roger.wang 1 rok pred
rodič
commit
57c24ccca5

+ 2 - 2
Makefile2

@@ -17,12 +17,12 @@ build:
 	CGO_ENABLED=1 go build -gcflags="all=-N -l" -buildvcs=false -o euspace
 c:
 	#docker exec -it 9d928d96d4d0 sh -c 'cd /opt/github/euspace/ebpftracer && sh build.sh${PARAMS}'
-	docker exec -it 0fec3217d6da sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
+	docker exec -it 3c1a4817a7ed sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
 c-build: c
 
 go-build:
 	#ssh [email protected] 'export https_proxy=http://10.0.22.50:4780 && source ~/.g/env && cd /opt/github/euspace && make -f Makefile2 build'
-	docker exec -it 0fec3217d6da bash -c 'cd /opt/github/euspace && source ~/.g/env && make -f Makefile2 build'
+	docker exec -it 3c1a4817a7ed bash -c 'cd /opt/github/euspace && source ~/.g/env && make -f Makefile2 build'
 go: go-build
 
 run:

+ 10 - 6
build.sh

@@ -5,7 +5,7 @@ make -f Makefile2 all debug=1 pid=1121
 
 # pid=`ps aux | grep ebpfdemo81 | grep -v grep | awk '{print $2}'`
 # echo $pid
-# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=go SEND=1 FILTER_PID=$pid WHITE_LIST=".*HandleFunc|.*main.*|.*serverHandler.*|.*ServeHTTP.*" ./euspace --listen="0.0.0.0:8123"
+# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=go SEND=1 FILTER_PID=$pid WHITE_LIST=".*HandleFunc|.*main.*|.*serverHandler.*|.*ServeHTTP.*" ./euspace --listen="0.0.0.0:8124"
 
 
 # pid=`ps aux | grep ./helloworld | grep -v grep | awk '{print $2}'`
@@ -22,11 +22,15 @@ make -f Makefile2 all debug=1 pid=1121
 # TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=go DBG_PATH="" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8123"
 
 
-# pid=`ps aux | grep CoreAoT | grep -v grep | awk '{print $2}'`
+pid=`ps aux | grep CoreAoT | grep -v grep | awk '{print $2}'`
+echo $pid
+CONFIG_SERVER=http://10.0.7.115:18080 DATA_SERVER=http://10.0.7.115:18080 TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=dotnet DBG_PATH="/data/roger/ebpfdemo/net8_demo/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT.dbg" SEND=1 FILTER_PID=$pid WHITE_LIST="main.*|Addwj.*|CoreAoT_Program___Main__.*" ./euspace  --listen="0.0.0.0:8124"
+
+# pid=`ps aux | grep SimpleHttpServer | grep -v grep | awk '{print $2}'`
 # echo $pid
-# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=dotnet DBG_PATH="/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT.dbg" SEND=1 FILTER_PID=$pid WHITE_LIST="main.*|Addwj.*|CoreAoT_Program___Main__.*" ./euspace  --listen="0.0.0.0:8123"
+# CONFIG_SERVER=http://10.0.7.115:18080 DATA_SERVER=http://10.0.7.115:18080 TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=java DBG_PATH="/data/roger/ebpfdemo/graalve_demo/SimpleHttpServer.dbg" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"
 
 
-pid=`pidof java`
-echo $pid
-TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=java DBG_PATH="" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"
+# pid=`pidof java`
+# echo $pid
+# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=java DBG_PATH="" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"

+ 58 - 2
containers/container.go

@@ -3,7 +3,6 @@ package containers
 import (
 	debugelf "debug/elf"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/utils"
 	"os"
 	"sort"
 	"strconv"
@@ -11,6 +10,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/coroot/coroot-node-agent/utils"
+
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 
 	"github.com/coroot/coroot-node-agent/cgroup"
@@ -1388,7 +1389,7 @@ func (c *Container) attachUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
 	case CodeTypeJava:
 		err = c.attachJVMUprobes(tracer, pid)
 	case CodeTypeJavaAot:
-		err = c.attachJVMUprobes(tracer, pid)
+		err = c.attachJavaAotUprobes(tracer, pid)
 	case CodeTypeGo:
 		err = c.attachTlsUprobes(tracer, pid)
 	case CodeTypeNetCoreAot:
@@ -1494,6 +1495,61 @@ func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) erro
 	return nil
 }
 
+func (c *Container) attachJavaAotUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
+	if common.IsOpenFilter() && !common.IsFilterPid(pid) {
+		return nil
+	}
+	p := c.processes[pid]
+	if p == nil {
+		return nil
+	}
+	codeType := c.GetCodeTypeFromCache(pid)
+	if !p.jvmUprobesChecked {
+		p.jvmUprobesChecked = true
+		rootfs := c.getRootfs()
+		tracer.InitKProcInfo(pid, &c.AppInfo)
+
+		// // TODO java Aot
+		// if codeType.IsJavaAotCode() {
+		// 	// check version
+		// 	libjavaso, err := utils.GetSoPath(pid, "libjava.so", rootfs)
+		// 	if err != nil {
+		// 		klog.WithError(err).Errorf("[attach] Failed get so path")
+		// 		return err
+		// 	}
+		// 	v, err := ebpftracer.GetJvmVersion(libjavaso)
+		// 	if err != nil {
+		// 		klog.WithError(err).Errorf("[attach] Failed get Java version")
+		// 		return err
+		// 	}
+		// 	c.AppInfo.Version = v
+		// 	major, minor, patch, err := ebpftracer.ParseVersion(v)
+		// 	klog.Infof("[attach] version: %s (Major: %d, Minor: %d, Patch: %d)", v, major, minor, patch)
+		// 	if major != 1 || minor != 8 {
+		// 		return fmt.Errorf("[attach] Unsupported Java version")
+		// 	}
+		// }
+
+		libNioProbes, err := tracer.AttachJavaAotNioReadUprobes(pid, codeType, rootfs)
+		if err != nil {
+			klog.Error(err)
+			return err
+		}
+		p.uprobes = append(p.uprobes, libNioProbes...)
+
+		libNetProbes, err := tracer.AttachJavaAotNetWriteUprobes(pid, rootfs)
+		if err != nil {
+			klog.Error(err)
+			return err
+		}
+		p.uprobes = append(p.uprobes, libNetProbes...)
+		//p.jvmUprobesChecked = true
+	} else {
+		klog.Infof("[attach] %s-%d already attach", codeType.String(), pid)
+	}
+	return nil
+}
+
 func (c *Container) errorClose(pid uint32, closeType int) {
 	p := c.processes[pid]
 	if p != nil {

+ 3 - 2
containers/container_apm.go

@@ -5,8 +5,6 @@ import (
 	"bytes"
 	"debug/elf"
 	"fmt"
-	"github.com/cilium/ebpf/link"
-	"github.com/coroot/coroot-node-agent/flags"
 	"os"
 	"path"
 	"sort"
@@ -14,6 +12,9 @@ import (
 	"strings"
 	"time"
 
+	"github.com/cilium/ebpf/link"
+	"github.com/coroot/coroot-node-agent/flags"
+
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"

+ 2 - 0
ebpftracer/ebpf/utrace/java/include/java_common.h

@@ -180,4 +180,6 @@ static __always_inline void span_context_to_cw_string_stream(struct apm_span_con
 	out += 3;
 }
 
+static __inline int insertHeader2(struct sock_t *map_data,void * jbytechar_ptr,void * len_from_rbp_ptr);
+
 #endif //EUSPACES_JAVA_COMMON_H

+ 106 - 0
ebpftracer/ebpf/utrace/java/net/client.probe.bpf.c

@@ -397,3 +397,109 @@ PROGUP(java_update_header)(struct pt_regs *ctx) {
 
 	return 0;
 }
+
+SEC("uprobe/javaaot_asmnop")
+int javaaot_asmnop(struct pt_regs *ctx) {
+	bpf_printk("enter the ret javaaot_asmnop\n");
+	unsigned long jhttpdata_ptr;
+	jhttpdata_ptr = (ctx)->rsi;
+	unsigned long len_from_rbp_ptr = (ctx)->rsp-0x8;
+	bpf_printk("[len_from_rbp_ptr] <0x%lx>", len_from_rbp_ptr);
+
+	// 捕获第六个参数 data_count
+	int data_count = PT_REGS_PARM3(ctx);
+	if (data_count < MIN_LEN) {
+		return 0;
+	}
+
+	cw_bpf_debug("[java client] data_count:%d\n", data_count);
+
+	void *len_ptr = 0;
+	cw_bpf_debug("[java client] address: len_from_rbp_ptr<0x%lx>\n", len_from_rbp_ptr);
+
+	bpf_probe_read(&len_ptr, sizeof(len_ptr), (void *) len_from_rbp_ptr);
+	cw_bpf_debug("[java client] [len_ptr] before addr<0x%lx>, %d \n", len_from_rbp_ptr, len_ptr);
+
+	if ((long) len_ptr != data_count) {
+		cw_bpf_debug("[java client] [len_ptr] check error.");
+		return 0;
+	}
+
+	int key = 0;
+	struct sock_t *map_data = bpf_map_lookup_elem(&socket_heap, &key);
+	if (!map_data) {
+		cw_bpf_debug("[java client] Failed to lookup socket_heap\n");
+		return 1;
+	}
+
+	// 获取jbytearray
+	__u32 tgid = (__u32) (bpf_get_current_pid_tgid() >> 32);
+	map_data->proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+	if (!map_data->proc_info) {
+		return 0;
+	}
+
+	// 读取用户空间数据到 map_data->payload
+	void *jbytechar_ptr = (void *) jhttpdata_ptr;
+	// cw_bpf_debug("[java client] [jbytechar_ptr] <0x%lx>", jbytechar_ptr);
+	long err = bpf_probe_read_user_str(map_data->payload, sizeof(map_data->payload), jbytechar_ptr);
+
+	if (err < 0) {
+		cw_bpf_debug("[java client] bpf_probe_read_user failed with return code: %d\n", err);
+		return 0;
+	}
+	map_data->size = data_count;
+	map_data->payload_char_p = (void *) jbytechar_ptr;
+	map_data->payload_len_p = (void *) len_from_rbp_ptr;
+
+	// http协议过滤
+	if (!is_http_request2(map_data->payload, map_data->size)) {
+		return -1;
+	}
+
+	// 查找 Header 开始位置
+	long header_start_native = 0x0a0d312e312f5054LL; // 小端序下的 "TP/1.1\r\n" (0x54 0x50 0x2f 0x31 0x2e 0x31 0x0d 0x0a)
+#pragma clang loop unroll(full)
+	for (u32 i = 0; i < 200 - 8; i++) {
+		long data = *(long long *) (map_data->payload + i);
+		if (data == header_start_native) {
+			map_data->header_offset_idx = i + 8;
+			break;
+		}
+	}
+
+	if (map_data->header_offset_idx == 0) {
+		cw_bpf_debug("[java client] header_offset_idx error:\n");
+		return 1;
+	}
+
+	map_data->end_str_len = data_count - map_data->header_offset_idx + 1;
+	u32 copy_size = L7_IOVEC_BUF_SIZE;
+	if (map_data->end_str_len > copy_size) {
+		u32 chunk = (u32) map_data->end_str_len / copy_size;
+		if (chunk > MAX_CHUNK) {
+			cw_bpf_debug("[java client] Exceeding the max_chunk:%d need_chunk:%d\n", MAX_CHUNK, chunk);
+			return 1;
+		}
+	}
+	// cw_bpf_debug("[java client] header_offset_idx:%d\n", map_data->header_offset_idx);
+
+	// tail payload保存
+	u32 tail_key = 1;
+	char *tail_payload = bpf_map_lookup_elem(&large_array_map, &tail_key);
+	if (!tail_payload) {
+		// cw_bpf_debug("[java client ]header ->end %s\n", tail_payload);
+		return 1;
+	}
+	__builtin_memset(tail_payload, 0, sizeof(char *));
+
+	bpf_probe_read_user_str(tail_payload, JAVA_MAX_BUFFER_SIZE,
+	                        (void *) (jbytechar_ptr + map_data->header_offset_idx));
+	// host查询
+	bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_DATA_JAVA_FIND_HOST_UP_IDX);
+	return 0;
+
+	bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_DATA_JAVA_UPDATE_HEADER_UP_IDX);
+
+	return 0;
+}

+ 71 - 24
ebpftracer/ebpf/utrace/netcore/net/client.probe.bpf.c

@@ -176,53 +176,100 @@ int netcore_asmnop(struct pt_regs *ctx) {
 	unsigned long len_from_rbp_ptr = (ctx)->rsp-0x8;
 	bpf_printk("[len_from_rbp_ptr] <0x%lx>", len_from_rbp_ptr);
 
-	// 定义 Map 键值和 Map 数据结构
+	// 捕获第六个参数 data_count
+	int data_count = PT_REGS_PARM3(ctx);
+	if (data_count < MIN_LEN) {
+		return 0;
+	}
+
+	cw_bpf_debug("[netcore client] data_count:%d\n", data_count);
+
+	void *len_ptr = 0;
+	cw_bpf_debug("[netcore client] address: len_from_rbp_ptr<0x%lx>\n", len_from_rbp_ptr);
+
+	bpf_probe_read(&len_ptr, sizeof(len_ptr), (void *) len_from_rbp_ptr);
+	cw_bpf_debug("[netcore client] [len_ptr] before addr<0x%lx>, %d \n", len_from_rbp_ptr, len_ptr);
+
+	if ((long) len_ptr != data_count) {
+		cw_bpf_debug("[netcore client] [len_ptr] check error.");
+		return 0;
+	}
+
 	int key = 0;
 	struct sock_t *map_data = bpf_map_lookup_elem(&socket_heap, &key);
 	if (!map_data) {
-		bpf_printk("Failed to lookup socket_heap\n");
+		cw_bpf_debug("[netcore client] Failed to lookup socket_heap\n");
 		return 1;
 	}
 
-	void *jbytechar_ptr = (void *) jhttpdata_ptr;
-	bpf_printk("[jbytechar_ptr] <0x%lx>", jbytechar_ptr);
-
-	unsigned long len_from_rbp;
-	bpf_probe_read_user(&len_from_rbp, sizeof(len_from_rbp), (void*)len_from_rbp_ptr);
-
-	bpf_printk("bpf_probe_read_user len_from_rbp: %ld\n", len_from_rbp);
+	// 获取jbytearray
+	__u32 tgid = (__u32) (bpf_get_current_pid_tgid() >> 32);
+	map_data->proc_info = bpf_map_lookup_elem(&proc_info_map, &tgid);
+	if (!map_data->proc_info) {
+		return 0;
+	}
 
+	// 读取用户空间数据到 map_data->payload
+	void *jbytechar_ptr = (void *) jhttpdata_ptr;
+	// cw_bpf_debug("[java client] [jbytechar_ptr] <0x%lx>", jbytechar_ptr);
 	long err = bpf_probe_read_user_str(map_data->payload, sizeof(map_data->payload), jbytechar_ptr);
 
 	if (err < 0) {
-		bpf_printk("bpf_probe_read_user failed with return code: %d\n", err);
+		cw_bpf_debug("[netcore client] bpf_probe_read_user failed with return code: %d\n", err);
 		return 0;
 	}
+	map_data->size = data_count;
+	map_data->payload_char_p = (void *) jbytechar_ptr;
+	map_data->payload_len_p = (void *) len_from_rbp_ptr;
 
-	bpf_printk("bpf_probe_read_user to lookup socket_heap: %s\n----------\n", map_data->payload);
-	map_data->size = len_from_rbp;
+	// http协议过滤
+	if (!is_http_request2(map_data->payload, map_data->size)) {
+		return -1;
+	}
 
 	// 查找 Header 开始位置
-	for (int i = 0; i < MAX_LEN - 9; i++) {
-		if (map_data->payload[i] == '1' &&
-		    map_data->payload[i + 1] == '.' &&
-		    map_data->payload[i + 2] == '1' &&
-		    map_data->payload[i + 3] == '\r' &&
-		    map_data->payload[i + 4] == '\n') {
-			map_data->header_offset_idx = i + 5;
+	long header_start_native = 0x0a0d312e312f5054LL; // 小端序下的 "TP/1.1\r\n" (0x54 0x50 0x2f 0x31 0x2e 0x31 0x0d 0x0a)
+#pragma clang loop unroll(full)
+	for (u32 i = 0; i < 200 - 8; i++) {
+		long data = *(long long *) (map_data->payload + i);
+		if (data == header_start_native) {
+			map_data->header_offset_idx = i + 8;
 			break;
 		}
 	}
 
 	if (map_data->header_offset_idx == 0) {
-		return -1;
+		cw_bpf_debug("[netcore client] header_offset_idx error:\n");
+		return 1;
 	}
 
-	// http协议过滤
-	if (!is_http_request2(map_data->payload, map_data->size)) {
-		return -1;
+	map_data->end_str_len = data_count - map_data->header_offset_idx + 1;
+	u32 copy_size = L7_IOVEC_BUF_SIZE;
+	if (map_data->end_str_len > copy_size) {
+		u32 chunk = (u32) map_data->end_str_len / copy_size;
+		if (chunk > MAX_CHUNK) {
+			cw_bpf_debug("[netcore client] Exceeding the max_chunk:%d need_chunk:%d\n", MAX_CHUNK, chunk);
+			return 1;
+		}
+	}
+	cw_bpf_debug("[netcore client] header_offset_idx:%d\n", map_data->header_offset_idx);
+
+	// tail payload保存
+	u32 tail_key = 1;
+	char *tail_payload = bpf_map_lookup_elem(&large_array_map, &tail_key);
+	if (!tail_payload) {
+		// cw_bpf_debug("[java client ]header ->end %s\n", tail_payload);
+		return 1;
 	}
+	__builtin_memset(tail_payload, 0, sizeof(char *));
+
+	bpf_probe_read_user_str(tail_payload, JAVA_MAX_BUFFER_SIZE,
+	                        (void *) (jbytechar_ptr + map_data->header_offset_idx));
+	// host查询
+	bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_DATA_JAVA_FIND_HOST_UP_IDX);
+	return 0;
+
+	bpf_tail_call(ctx, &NAME(progs_jmp_up_map), PROG_DATA_JAVA_UPDATE_HEADER_UP_IDX);
 
-	insertHeader2(map_data,jbytechar_ptr,(void *)len_from_rbp_ptr);
 	return 0;
 }

+ 151 - 0
ebpftracer/javaAot.go

@@ -0,0 +1,151 @@
+package ebpftracer
+
+import (
+	"debug/elf"
+	"errors"
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/cilium/ebpf/link"
+	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/aotinject"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
+)
+
+func (t *Tracer) AttachJavaAotNioReadUprobes(pid uint32, codeType CodeType, rootfs string) ([]link.Link, error) {
+	if t.DisableL7Tracing() {
+		return nil, nil
+	}
+	var links []link.Link
+	var bpath string
+
+	// 根据进程 pid 获取进程的可执行文件路径
+	bpath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
+	if err != nil {
+		log.Fatalf("Failed to read proc path: %v", err)
+	}
+	fmt.Printf("procPath: %s\n", bpath)
+	//bpath = rootfs + bpath
+	klog.Infof("[attach] find the nio.so path is  %s", bpath)
+
+	readInfo, _ := aotinject.GetProcessFunctionInfo(int(pid), bpath, symbolsocketRead0)
+	if readInfo == nil {
+		return nil, fmt.Errorf("can not find %s", symbolsocketRead0)
+	}
+	// 打印 readInfo
+	klog.Infof("[attach] AttachJavaAotNioReadUprobes readInfo %v", readInfo)
+
+	ef, err := elf.Open(bpath)
+	if err != nil {
+		klog.Errorf("[attach] open elf: %v", err)
+		return nil, err
+	}
+	defer ef.Close()
+
+	textSection := ef.Section(".text")
+	if textSection == nil {
+		return nil, errors.New("can not find .text section")
+	}
+	textSectionData, err := textSection.Data()
+	if err != nil {
+		return nil, err
+	}
+	textSectionLen := uint64(len(textSectionData) - 1)
+	// 打印 textSectionLen
+	klog.Infof("[attach] AttachJavaAotNioReadUprobes textSectionLen %d{0x%x}", textSectionLen, textSection.Addr)
+
+	sStart := readInfo.Offset - textSection.Addr
+	sEnd := sStart + readInfo.Size
+	if sEnd > textSectionLen {
+		klog.Infof("[attach] AttachJavaAotNioReadUprobes sEnd > textSectionLen %d, %d", textSectionLen, sEnd)
+		return nil, fmt.Errorf("can not find %s", symbolsocketRead0)
+	}
+	sBytes := textSectionData[sStart:sEnd]
+	// 打印 sBytes
+	returnOffsets := getCallNextMoveOffsets(ef.Machine, sBytes)
+	if len(returnOffsets) == 0 {
+		return nil, fmt.Errorf("failed to find call in uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0")
+	}
+	klog.Infof("[attach] java symbol offset is  %v", returnOffsets)
+	for _, offset := range returnOffsets {
+		klog.Infof("[attach] java symbol offset is  %d", offset)
+		ex, err := link.OpenExecutable(bpath)
+		if err != nil {
+			klog.Errorf("[attach] open executable: %v", err)
+			return nil, err
+		}
+		l, err := ex.Uprobe(readInfo.Name, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &link.UprobeOptions{Address: readInfo.Offset, Offset: uint64(offset), PID: int(pid)})
+		if err != nil {
+			return nil, fmt.Errorf("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe")
+		}
+
+		links = append(links, l)
+	}
+
+	if len(links) == 0 {
+		return nil, fmt.Errorf("no links found for %s", bpath)
+	}
+	klog.WithField("pid", pid).Infof("[attach] libnio attached!")
+	return links, nil
+}
+
+func (t *Tracer) AttachJavaAotNetWriteUprobes(pid uint32, rootfs string) ([]link.Link, error) {
+	if t.DisableL7Tracing() {
+		return nil, nil
+	}
+
+	// 根据进程 pid 获取进程的可执行文件路径
+	procPath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
+	if err != nil {
+		log.Fatalf("Failed to read proc path: %v", err)
+	}
+	fmt.Printf("procPath: %s\n", procPath)
+
+	sendFunctionName := "NET_Send"
+	injectFunctionName := "Java_java_net_SocketOutputStream_socketWrite0"
+
+	// NETCoreAOTInject
+	aotInjector := aotinject.AOTInjector{
+		PID:                 int(pid),
+		SendFunctionLibPath: procPath,
+		SendFunctionName:    sendFunctionName,
+		InjectLibPath:       procPath,
+		InjectFunctionName:  injectFunctionName,
+		CWLibName:           "libmylib.so",
+		CWFunctionName:      "asmnop",
+		CWLibPath:           "/data/roger/ebpfdemo/mylib/libmylib.so",
+	}
+	// 打印 aotInjector
+	fmt.Printf("aotInjector: %v\n", aotInjector)
+	hookOffset, err := aotinject.AotInject(aotInjector)
+	// 打印 hookOffset
+	fmt.Printf("hookOffset: %d\n", hookOffset)
+
+	if (hookOffset == 0) || (err != nil) {
+		fmt.Println("failed to inject SystemNative_Send", err)
+		return nil, err
+	}
+
+	var links []link.Link
+	ex, err := link.OpenExecutable(aotInjector.CWLibPath)
+	if err != nil {
+		return nil, err
+	}
+	opt := link.UprobeOptions{
+		Offset: uint64(hookOffset),
+		PID:    int(pid),
+	}
+	upread02, err := ex.Uprobe(aotInjector.CWFunctionName, t.uprobes["javaaot_asmnop"], &opt)
+	if err != nil {
+		return nil, err
+	}
+	links = append(links, upread02)
+
+	if len(links) == 0 {
+		return nil, nil
+	}
+	fmt.Println("netcore client uprobes attached", pid)
+
+	return links, nil
+}

+ 50 - 16
ebpftracer/netcore.go

@@ -16,6 +16,7 @@ import (
 	"fmt"
 
 	"github.com/cilium/ebpf/link"
+	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/aotinject"
 )
 
 const (
@@ -167,12 +168,17 @@ func (t *Tracer) AttachNetCoreNetReadUprobes(pid uint32) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
 		return nil, nil
 	}
-	libPath := "/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT"
+	// 根据进程 pid 获取进程的可执行文件路径
+	procPath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
+	if err != nil {
+		log.Fatalf("Failed to read proc path: %v", err)
+	}
+	fmt.Printf("procPath: %s\n", procPath)
 	var links []link.Link
-	ex, err := link.OpenExecutable(libPath)
+	ex, err := link.OpenExecutable(procPath)
 
 	// 获取函数的偏移量
-	functionSym, err := t.getFunctionOffsetDBG(libPath+".dbg", netcoresymbolsocketRead0)
+	functionSym, err := t.getFunctionOffsetDBG(procPath+".dbg", netcoresymbolsocketRead0)
 
 	l, err := ex.Uprobe(functionSym.Name, t.uprobes["SystemNative_Receive"], &link.UprobeOptions{Address: functionSym.Value, Offset: 113})
 	if err != nil {
@@ -193,23 +199,45 @@ func (t *Tracer) AttachNetCoreNetWriteUprobes(pid uint32) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
 		return nil, nil
 	}
-	//
-	//if pid != 251719 {
-	//	return nil
-	//}
 
-	var libnetSo = "/data/roger/han/libmylib.so"
-	var sys = "asmnop"
+	// 根据进程 pid 获取进程的可执行文件路径
+	procPath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
+	if err != nil {
+		log.Fatalf("Failed to read proc path: %v", err)
+	}
+	fmt.Printf("procPath: %s\n", procPath)
+
+	sendFunctionName := "send@plt"
+	injectFunctionName := "SystemNative_Send"
+
+	// NETCoreAOTInject
+	netCoreAotInjector := aotinject.AOTInjector{
+		PID:                 int(pid),
+		SendFunctionLibPath: procPath,
+		SendFunctionName:    sendFunctionName,
+		InjectLibPath:       procPath,
+		InjectFunctionName:  injectFunctionName,
+		CWLibName:           "libmylib.so",
+		CWFunctionName:      "asmnop",
+		CWLibPath:           "/data/roger/ebpfdemo/mylib/libmylib.so",
+	}
+	hookOffset, err := aotinject.AotInject(netCoreAotInjector)
+
+	if (hookOffset == 0) || (err != nil) {
+		fmt.Println("failed to inject SystemNative_Send", err)
+		return nil, err
+	}
+
 	var links []link.Link
-	ex, err := link.OpenExecutable(libnetSo)
+	ex, err := link.OpenExecutable(netCoreAotInjector.CWLibPath)
 	if err != nil {
 		return nil, err
 	}
 	opt := link.UprobeOptions{
-		Offset: 16,
+		Offset: uint64(hookOffset),
 		PID:    int(pid),
 	}
-	upread02, err := ex.Uprobe(sys, t.uprobes["netcore_asmnop"], &opt)
+	upread02, err := ex.Uprobe(netCoreAotInjector.CWFunctionName, t.uprobes["netcore_asmnop"], &opt)
 	if err != nil {
 		return nil, err
 	}
@@ -263,9 +291,15 @@ func SplitByteByDelimiter(data []byte) []byte {
 
 func (t *Tracer) AttachNetCoreNetThreadUprobes(pid uint32) []link.Link {
 	// uprobes := []tracer.Uprobe{}
-	libPath := "/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT"
-	binFile, err := os.Open(libPath + ".dbg")
-	binFile2, err := os.Open(libPath)
+	// 根据进程 pid 获取进程的可执行文件路径
+	procPath, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
+	if err != nil {
+		log.Fatalf("Failed to read proc path: %v", err)
+	}
+	fmt.Printf("procPath: %s\n", procPath)
+
+	binFile, err := os.Open(procPath + ".dbg")
+	binFile2, err := os.Open(procPath)
 	if err != nil {
 		return nil
 	}
@@ -277,7 +311,7 @@ func (t *Tracer) AttachNetCoreNetThreadUprobes(pid uint32) []link.Link {
 	// 获取所有符号表
 	symbols, _ := elfFile.Symbols()
 
-	ex, err := link.OpenExecutable(libPath)
+	ex, err := link.OpenExecutable(procPath)
 
 	words := []string{
 		"Microsoft_AspNetCore_Server_Kestrel_Core_Microsoft_AspNetCore_Server_Kestrel_Core_Internal_HttpConnectionMiddleware_1<System___Canon>__OnConnectionAsync", "Microsoft_AspNetCore_Server_Kestrel_Transport_Sockets_Microsoft_AspNetCore_Server_Kestrel_Transport_Sockets_Internal_SocketConnection__DoSend_d__28__MoveNext", "System_Net_Http_System_Net_Http_HttpClient__SendAsync_2",

+ 445 - 0
ebpftracer/tracer/aotinject/inject_aot.go

@@ -0,0 +1,445 @@
+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)
+}

+ 375 - 0
ebpftracer/tracer/aotinject/inject_asm_code_amd64.go

@@ -0,0 +1,375 @@
+//go:build linux && amd64
+// +build linux,amd64
+
+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 (
+	"debug/elf"
+	"errors"
+	"fmt"
+	"syscall"
+	"unsafe"
+
+	"golang.org/x/arch/x86/x86asm"
+)
+
+var PID string
+
+const jumpBackAddrOffset = 15
+const nopEntryOffset = 12
+const nopLen = 1
+const longJumpSize = 17
+
+type ProcessMapsInfo struct {
+	Start, End uint64
+	Path       string
+}
+type ProcessFunctionInfo struct {
+	Name        string
+	Offset      uint64
+	Start, Size uint64
+}
+
+type InstMemStruct struct {
+	InstStartAddr uint64
+	InstEndAddr   uint64
+	CodeArray     []byte
+	InstCodeArray [][]byte
+	InsertIndex   int
+}
+
+// 解析code中的指令,并打印每一条指令
+func parseAndPrintInstructions(code []byte) {
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := x86asm.Decode(code[pc:], 64)
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+
+		fmt.Printf("0x%x:[%d] %s\n", pc, pc, inst.String())
+		pc += uint64(inst.Len)
+	}
+}
+
+func findSendFunctionNameAddr(SymAddr uint64, code []byte, sendFunctionName string, sendSymAddr uint64) (uint64, uint64, error) {
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := x86asm.Decode(code[pc:], 64)
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+
+		fmt.Printf("0x%x:[%d] %s\n", SymAddr+pc, pc, inst.String())
+		if inst.Op == x86asm.CALL {
+			if v, ok := inst.Args[0].(x86asm.Rel); ok {
+				fmt.Printf("%s\n", inst.Args[0].String())
+				if SymAddr+pc+uint64(v)+5 == sendSymAddr {
+					fmt.Printf("Found send function call at 0x%x\n", SymAddr+pc)
+					return SymAddr + pc, pc, nil
+				}
+			}
+		}
+
+		pc += uint64(inst.Len)
+	}
+	return 0, 0, errors.New("Send function Offset not found")
+}
+
+// 获取 Send 函数附近的内存数据,用于长跳转到 libmylib 中,需要凑够 17 个字节,先向上查找,再向下查找
+func findMemForLongJump(SymAddr uint64, code []byte, sendFunctionName string, sendSymAddr uint64) (InstMemStruct, error) {
+	// 定义一个结构体,储存 pc、指令、和指令长度
+	type InstStruct struct {
+		pc   uint64
+		inst x86asm.Inst
+		len  int
+	}
+	// 定义个字节数组用于储存遍历出来的指令,字节数组
+	var codeArray []byte
+	// 定义一个字节数组的数组,用于储存遍历出来的每个指令的字节内容
+	var InstCodeArray [][]byte
+	// 定义一个InstStruct数组,用于储存遍历出来的指令
+	var instArray []InstStruct
+	// 定义一个标志记录记录找到的Send函数在instArray数组的位置
+	var sendIndex int
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := x86asm.Decode(code[pc:], 64)
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+		// 将指令和指令长度存入InstStruct结构体中
+		instArray = append(instArray, InstStruct{pc: pc, inst: inst, len: inst.Len})
+		// 如果地址等于Send函数的地址,记录下Send函数在instArray数组的位置
+		if SymAddr+pc == sendSymAddr {
+			sendIndex = len(instArray) - 1
+		}
+		pc += uint64(inst.Len)
+	}
+	// 打印 sendIndex 和 instArray 的长度和 Send 函数的指令
+	fmt.Printf("sendIndex: %d, instArray len: %d, Send Function: %s\n", sendIndex, len(instArray), instArray[sendIndex].inst.String())
+	InstStartAddr := uint64(0)
+	InstEndAddr := uint64(sendSymAddr) + 5
+	// 从Send函数开始向上查找,凑够17个字节
+	for i := sendIndex; i >= 0; i-- {
+		if i != sendIndex && instArray[i].inst.Op == x86asm.CALL || instArray[i].inst.Op == x86asm.JMP || instArray[i].inst.Op == x86asm.JE || instArray[i].inst.Op == x86asm.JG || instArray[i].inst.Op == x86asm.JGE || instArray[i].inst.Op == x86asm.JL || instArray[i].inst.Op == x86asm.JLE || instArray[i].inst.Op == x86asm.JNE {
+			break
+		}
+		// 将指令存入codeArray数组,指令应该从 code 中的pc开始,长度为instArray[i].len
+		codeArray = append(codeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)]...)
+		InstCodeArray = append(InstCodeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)])
+		// 记录当前指令的地址
+		InstStartAddr = SymAddr + instArray[i].pc
+		// 打印本次循环的具体指令和指令长度
+		fmt.Printf("inst: %s\n", instArray[i].inst.String())
+		// 打印codeArray数组的长度和内容
+		fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+		// 如果codeArray数组长度大于等于17,或者 该指令代码等于 Call、jmp、jne、je、jg、jge、jl、jle、jne、等跳转指令,跳出循环,用于跳转的指令有哪些,可以根据实际情况添加
+		if len(codeArray) >= longJumpSize {
+			fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+			// 打印InstCodeArray数组的长度和内容
+			fmt.Printf("InstCodeArray len: %d, InstCodeArray: %v\n", len(InstCodeArray), InstCodeArray)
+			break
+		}
+	}
+	// 将 InstCodeArray 数组进行倒序
+	for i := 0; i < len(InstCodeArray)/2; i++ {
+		InstCodeArray[i], InstCodeArray[len(InstCodeArray)-1-i] = InstCodeArray[len(InstCodeArray)-1-i], InstCodeArray[i]
+	}
+	insertIndex := len(InstCodeArray) - 1
+	// 如果 codeArray 的长度没有凑够 17 个字节,则从Send函数开始向下查找,继续凑够17个字节
+	if len(codeArray) < longJumpSize {
+		// 从Send函数开始向下查找,凑够17个字节
+		for i := sendIndex + 1; i < len(instArray); i++ {
+			if instArray[i].inst.Op == x86asm.CALL || instArray[i].inst.Op == x86asm.JMP || instArray[i].inst.Op == x86asm.JE || instArray[i].inst.Op == x86asm.JG || instArray[i].inst.Op == x86asm.JGE || instArray[i].inst.Op == x86asm.JL || instArray[i].inst.Op == x86asm.JLE || instArray[i].inst.Op == x86asm.JNE {
+				break
+			}
+			// 将指令存入codeArray数组,指令应该从 code 中的pc开始,长度为instArray[i].len
+			codeArray = append(codeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)]...)
+			InstCodeArray = append(InstCodeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)])
+			// 记录当前指令的地址
+			InstEndAddr = SymAddr + instArray[i].pc + uint64(instArray[i].len)
+			// 打印本次循环的具体指令和指令长度
+			fmt.Printf("inst: %s\n", instArray[i].inst.String())
+			// 打印codeArray数组的长度和内容
+			fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+			// 如果codeArray数组长度大于等于17,或者 该指令代码等于 Call、jmp、jne、je、jg、jge、jl、jle、jne、等跳转指令,跳出循环,用于跳转的指令有哪些,可以根据实际情况添加
+			if len(codeArray) >= longJumpSize {
+				fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+				// 打印InstCodeArray数组的长度和内容
+				fmt.Printf("InstCodeArray len: %d, InstCodeArray: %v\n", len(InstCodeArray), InstCodeArray)
+				break
+			}
+		}
+	}
+	// 打印最终的codeArray数组的长度和内容
+	fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+	// 打印最终的InstAddr地址
+	fmt.Printf("InstAddr: %x-%x  Len: %d, InstCodeArray: %v, InstCodeArray len: %d\n", InstStartAddr, InstEndAddr, len(codeArray), InstCodeArray, len(InstCodeArray))
+	instMemStruct := InstMemStruct{InstStartAddr: InstStartAddr, InstEndAddr: InstEndAddr, CodeArray: codeArray, InstCodeArray: InstCodeArray, InsertIndex: insertIndex}
+	if len(codeArray) < longJumpSize {
+		return instMemStruct, errors.New("Not enough memory for long jump")
+	}
+	return instMemStruct, nil
+}
+
+// 从 PLT 中获取函数的地址,先从改进程的 maps 中的 glibc 中获取对应的函数地址,在反过来从 plt 段中的 jmp 中计算出该函数的地址
+func getFunctionOffsetPLT(pid int, libPath, functionName string) (elf.Symbol, error) {
+	// 获取进程 maps 中 libc.so 的基地址
+	processLibcMapsInfo, err := getProcessMapsInfo(pid, "libc.so.6")
+	// 获取 libc 中 send 函数的地址
+	libcFunction, err := getFunctionOffset(processLibcMapsInfo.Path, functionName)
+	// 打印 libc 中 send 函数的地址
+	fmt.Printf("libc send function: %s, addr: %x, addr: %x\n", functionName, libcFunction.Value, libcFunction.Value+processLibcMapsInfo.Start)
+	processMapsInfo, err := getProcessMapsInfo(pid)
+	// 打印进程 maps 中的地址
+	fmt.Printf("process maps: %s, addr: %x\n", processMapsInfo.Path, processMapsInfo.Start)
+	// 解析 libPath 中的 plt 段,遍历所有的 plt 段的地址,找到 所有 jmp 指令所跳转的地址为 libc 中 send 函数的地址
+	elfFile, err := elf.Open(libPath)
+	if err != nil {
+		return elf.Symbol{}, fmt.Errorf("failed to open ELF file: %v", err)
+	}
+	defer elfFile.Close()
+	// 遍历 plt 段
+	pltSection := elfFile.Section(".plt")
+	if pltSection == nil {
+		return elf.Symbol{}, errors.New(".plt section not found")
+	}
+	pltData, err := pltSection.Data()
+	if err != nil {
+		return elf.Symbol{}, fmt.Errorf("failed to read .plt section data: %v", err)
+	}
+	entrySize := 16
+	// 计算 plt 段中一共有多少 plt 入口,使用 plt 段的大小除以 plt 入口的大小
+	// plt 入口的数量为 len(pltData)/entrySize
+	entryNum := len(pltData) / entrySize
+	baseJmpAddr := uint64(0)
+	isSetBaseJmpAddr := false
+	// 初始化一个 map,key 为指向的地址,value 为 plt 段中的地址
+	pltEntryMap := make(map[uint64]uint64)
+	// 遍历 plt 段
+	for i := 0; i < len(pltData); i += int(entrySize) {
+		// 获取 plt 段中的地址
+		addr := pltSection.Addr + uint64(i)
+		// 打印 addr
+		fmt.Printf("addr: %x\n", addr)
+		// 获取 plt 段中的 jmp 指令
+		jmpInst := pltData[i : i+int(entrySize)]
+		// 解析 jmp 指令
+		inst, err := x86asm.Decode(jmpInst, 64)
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+		// 判断 inst 是否为 jmp 指令
+		if inst.Op == x86asm.JMP {
+			jmpAddr := addr + uint64(inst.Len) + uint64(int32(inst.Args[0].(x86asm.Mem).Disp))
+			pltEntryMap[jmpAddr] = addr
+
+			if isSetBaseJmpAddr == false {
+				// baseAddr = addr
+				baseJmpAddr = jmpAddr
+				isSetBaseJmpAddr = true
+			}
+		}
+	}
+
+	var data []byte
+	data = make([]byte, 8*(entryNum+1))
+	// 使用 ptrace attach 目标进程
+	fmt.Printf("Attach Process: %d\n", pid)
+	if err := syscall.PtraceAttach(pid); err != nil {
+		fmt.Printf("PtraceAttach Err: %v\n", err)
+	}
+	// 等待目标进程停止
+	if _, err := syscall.Wait4(pid, nil, 0, nil); err != nil {
+		fmt.Printf("wait4: %v", err)
+	}
+	dataLen, _ := syscall.PtracePeekData(pid, uintptr(baseJmpAddr+processMapsInfo.Start), data)
+
+	fmt.Printf("Ptrace DETACH: %d\n", pid)
+	// 恢复执行
+	if err = syscall.PtraceDetach(pid); err != nil {
+		fmt.Printf("ptrace DETACH: %v", err)
+	}
+
+	// 按 uint64 数组遍历 data
+	for i := 0; i < dataLen; i += 8 {
+		// 获取 data 中的地址
+		addr := *(*uint64)(unsafe.Pointer(&data[i]))
+		// 打印 data 中的地址
+		fmt.Printf("addr: 0x%x\n", addr)
+		// 如果 data 中的地址等于 libc 中 send 函数的地址,返回该地址
+		if processLibcMapsInfo.Start+libcFunction.Value == addr {
+			fmt.Printf("Found %s at address: 0x%x\n", functionName, baseJmpAddr)
+			fmt.Printf("Found %s from address: 0x%x[0x%x]\n", functionName, uint64(i)+baseJmpAddr, processMapsInfo.Start+uint64(i)+baseJmpAddr)
+			// 判断 pltEntryMap 中是否存在 baseJmpAddr,如果存在,返回 baseJmpAddr
+			if value, ok := pltEntryMap[uint64(i)+baseJmpAddr]; ok {
+				fmt.Printf("Found %s at address on plt: 0x%x\n", functionName, value)
+				return elf.Symbol{Name: functionName, Value: value, Size: 0}, nil
+			}
+		}
+	}
+	return elf.Symbol{}, fmt.Errorf("function %s not found", functionName)
+}
+
+// 生成一个函数,用于生成长跳指令插入,使用 r11 寄存器,使用前需要保存 r11 寄存器的值,使用后恢复 r11 寄存器的值,跳转的指令从参数传入,第二个参数为指令长度,如果指令长度不够,需要在后面添加 nop 指令
+func generateLongJumpCode(cwFunctionAddr uint64, length int, saveR11 bool) []byte {
+	jumpCode := []byte{}
+
+	if saveR11 {
+		jumpCode = append(jumpCode, 0x41, 0x53) // push r11
+	}
+
+	jumpCode = append(jumpCode,
+		0x49, 0xbb, byte(cwFunctionAddr), byte(cwFunctionAddr>>8), byte(cwFunctionAddr>>16), byte(cwFunctionAddr>>24),
+		byte(cwFunctionAddr>>32), byte(cwFunctionAddr>>40), byte(cwFunctionAddr>>48), byte(cwFunctionAddr>>56), // movabs r11, cwFunctionAddr
+		0x41, 0xff, 0xe3, // jmp r11
+	)
+
+	if saveR11 {
+		jumpCode = append(jumpCode, 0x41, 0x5b) // pop r11
+	}
+
+	// Add NOP instructions if the length is not enough
+	for len(jumpCode) < length {
+		jumpCode = append(jumpCode, 0x90) // NOP instruction
+	}
+
+	return jumpCode
+}
+
+// 生成新指令,用于替换原指令,一个参数是原来的指令 instCodeArray,另一个参数是跳转回去的地址,第三个参数是原始 Send 函数的地址,第四个参数是在哪个指令前插入自定义指令
+func generateNewCode(instCodeArray [][]byte, jumpBackAddr uint64, sendAddr uint64, insertIndex int) ([]byte, int) {
+	newCode := []byte{}
+	hookOffset := 0
+	for i, inst := range instCodeArray {
+		if i == insertIndex {
+			/**
+			约定从栈顶拿8个字节储存 ebpf 中配置的 header 长度:先备份 rdx 到 -0x08(%rsp)
+			待 ebpf 将值写到 -0x08(%rsp) 之后,再从 -0x08(%rsp) 赋值到 rdx
+			mov     %rdx, -0x08(%rsp)
+			mov     -0x08(%rsp), %rdx
+			**/
+			newCode = append(newCode, 0x48, 0x89, 0x54, 0x24, 0xf8) // mov %rdx, -0x08(%rsp)
+			hookOffset = len(newCode)
+			newCode = append(newCode, 0x90)                         // nop
+			newCode = append(newCode, 0x48, 0x8b, 0x54, 0x24, 0xf8) // mov -0x08(%rsp), %rdx
+			/**
+			生成 Call 指令利用 r11 寄存器进程长调用调用原来的 Send 函数
+			**/
+			CallSendCode := []byte{
+				0x41, 0x53, // push r11
+				0x49, 0xbb, byte(sendAddr), byte(sendAddr >> 8), byte(sendAddr >> 16), byte(sendAddr >> 24), byte(sendAddr >> 32), byte(sendAddr >> 40), byte(sendAddr >> 48), byte(sendAddr >> 56), // movabs r11, sendAddr
+				0x41, 0xff, 0xd3, // call r11
+				0x41, 0x5b, // pop r11
+			}
+			newCode = append(newCode, CallSendCode...)
+			// 生成长跳指令,跳转回去
+			newCode = append(newCode, generateLongJumpCode(jumpBackAddr, longJumpSize, false)...)
+			continue
+		}
+		newCode = append(newCode, inst...)
+	}
+	return newCode, hookOffset
+}
+
+// 查找nop函数中hookOffset的位置
+func findNopFunctionHookOffset(pid int, cwSym *ProcessFunctionInfo) (int, error) {
+	// 定义一个字节数组内容:0x48, 0x8b, 0x54, 0x24, 0xf8
+	nopCode := []byte{0x48, 0x8b, 0x54, 0x24, 0xf8}
+	code, err := readMemory(pid, cwSym.Start+nopEntryOffset, cwSym.Size-nopEntryOffset)
+	if err != nil {
+		fmt.Printf("readMemory error: %v\n", err)
+		return 0, err
+	}
+	fmt.Printf("nopCode: %v\n", code)
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := x86asm.Decode(code[pc:], 64)
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+		// 打印对应指令的字节码
+		fmt.Printf("%s[%d]-[%v]\n", inst.String(), pc, code[pc:pc+uint64(inst.Len)])
+		// 如果指令的字节码等于nopCode,返回当前的pc
+		if string(code[pc:pc+uint64(inst.Len)]) == string(nopCode) {
+			return int(pc) - nopLen, nil
+		}
+		pc += uint64(inst.Len)
+	}
+	return 0, errors.New("hookOffset not found")
+}

+ 393 - 0
ebpftracer/tracer/aotinject/inject_asm_code_arm64.go

@@ -0,0 +1,393 @@
+//go:build linux && arm64
+// +build linux,arm64
+
+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 (
+	"debug/elf"
+	"errors"
+	"fmt"
+	"syscall"
+	"unsafe"
+
+	"golang.org/x/arch/arm64/arm64asm"
+	"golang.org/x/arch/x86/x86asm"
+)
+
+var PID string
+
+// 定义一个全局变量,类型为 uint64,值为 4
+const instLen = 4
+const jumpBackAddrOffset = 24
+const nopEntryOffset = 12
+const nopLen = 4
+const longJumpSize = 28
+
+type ProcessMapsInfo struct {
+	Start, End uint64
+	Path       string
+}
+type ProcessFunctionInfo struct {
+	Name        string
+	Start, Size uint64
+}
+
+type InstMemStruct struct {
+	InstStartAddr uint64
+	InstEndAddr   uint64
+	CodeArray     []byte
+	InstCodeArray [][]byte
+	InsertIndex   int
+}
+
+// 解析code中的指令,并打印每一条指令
+func parseAndPrintInstructions(code []byte) {
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := arm64asm.Decode(code[pc:])
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+
+		fmt.Printf("0x%x:[%d] %s\n", pc, pc, inst.String())
+		pc += instLen
+	}
+}
+
+func findSendFunctionNameAddr(SymAddr uint64, code []byte, sendFunctionName string, sendSymAddr uint64) (uint64, uint64, error) {
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := arm64asm.Decode(code[pc:])
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+
+		fmt.Printf("0x%x:[%d] %s, %v\n", SymAddr+pc, pc, inst.String(), inst.Args[0].String())
+		if inst.Op == arm64asm.BL {
+			if v, ok := inst.Args[0].(arm64asm.PCRel); ok {
+				fmt.Printf("%s\n", inst.Args[0].String())
+				if SymAddr+pc+uint64(v) == sendSymAddr {
+					fmt.Printf("Found send function call at 0x%x\n", SymAddr+pc)
+					return SymAddr + pc, pc, nil
+				}
+			}
+		}
+
+		pc += instLen
+	}
+	return 0, 0, errors.New("Send function Offset not found")
+}
+
+// 获取 Send 函数附近的内存数据,用于长跳转到 libmylib 中,需要凑够 17 个字节,先向上查找,再向下查找
+func findMemForLongJump(SymAddr uint64, code []byte, sendFunctionName string, sendSymAddr uint64) (InstMemStruct, error) {
+	// 定义一个结构体,储存 pc、指令、和指令长度
+	type InstStruct struct {
+		pc   uint64
+		inst arm64asm.Inst
+		len  int
+	}
+	// 定义个字节数组用于储存遍历出来的指令,字节数组
+	var codeArray []byte
+	// 定义一个字节数组的数组,用于储存遍历出来的每个指令的字节内容
+	var InstCodeArray [][]byte
+	// 定义一个InstStruct数组,用于储存遍历出来的指令
+	var instArray []InstStruct
+	// 定义一个标志记录记录找到的Send函数在instArray数组的位置
+	var sendIndex int
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := arm64asm.Decode(code[pc:])
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+		// 将指令和指令长度存入InstStruct结构体中
+		instArray = append(instArray, InstStruct{pc: pc, inst: inst, len: instLen})
+		// 如果地址等于Send函数的地址,记录下Send函数在instArray数组的位置
+		if SymAddr+pc == sendSymAddr {
+			sendIndex = len(instArray) - 1
+		}
+		pc += instLen
+	}
+	// 打印 sendIndex 和 instArray 的长度和 Send 函数的指令
+	fmt.Printf("sendIndex: %d, instArray len: %d, Send Function: %s\n", sendIndex, len(instArray), instArray[sendIndex].inst.String())
+	InstStartAddr := uint64(0)
+	InstEndAddr := uint64(sendSymAddr) + 4
+	// 从Send函数开始向上查找,凑够17个字节
+	for i := sendIndex; i >= 0; i-- {
+		if i != sendIndex && instArray[i].inst.Op == arm64asm.BL || instArray[i].inst.Op == arm64asm.B || instArray[i].inst.Op == arm64asm.BLR || instArray[i].inst.Op == arm64asm.CBNZ || instArray[i].inst.Op == arm64asm.CBZ || instArray[i].inst.Op == arm64asm.RET || instArray[i].inst.Op == arm64asm.TBZ || instArray[i].inst.Op == arm64asm.TBNZ {
+			break
+		}
+		// 将指令存入codeArray数组,指令应该从 code 中的pc开始,长度为instArray[i].len
+		codeArray = append(codeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)]...)
+		InstCodeArray = append(InstCodeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)])
+		// 记录当前指令的地址
+		InstStartAddr = SymAddr + instArray[i].pc
+		// 打印本次循环的具体指令和指令长度
+		fmt.Printf("inst: %s\n", instArray[i].inst.String())
+		// 打印codeArray数组的长度和内容
+		fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+		// 如果codeArray数组长度大于等于17,或者 该指令代码等于 Call、jmp、jne、je、jg、jge、jl、jle、jne、等跳转指令,跳出循环,用于跳转的指令有哪些,可以根据实际情况添加
+		if len(codeArray) >= longJumpSize {
+			fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+			// 打印InstCodeArray数组的长度和内容
+			fmt.Printf("InstCodeArray len: %d, InstCodeArray: %v\n", len(InstCodeArray), InstCodeArray)
+			break
+		}
+	}
+	// 将 InstCodeArray 数组进行倒序
+	for i := 0; i < len(InstCodeArray)/2; i++ {
+		InstCodeArray[i], InstCodeArray[len(InstCodeArray)-1-i] = InstCodeArray[len(InstCodeArray)-1-i], InstCodeArray[i]
+	}
+	insertIndex := len(InstCodeArray) - 1
+	// 如果 codeArray 的长度没有凑够 17 个字节,则从Send函数开始向下查找,继续凑够17个字节
+	if len(codeArray) < longJumpSize {
+		// 从Send函数开始向下查找,凑够17个字节
+		for i := sendIndex + 1; i < len(instArray); i++ {
+			if instArray[i].inst.Op == arm64asm.BL || instArray[i].inst.Op == arm64asm.B || instArray[i].inst.Op == arm64asm.BLR || instArray[i].inst.Op == arm64asm.CBNZ || instArray[i].inst.Op == arm64asm.CBZ || instArray[i].inst.Op == arm64asm.RET || instArray[i].inst.Op == arm64asm.TBZ || instArray[i].inst.Op == arm64asm.TBNZ {
+				break
+			}
+			// 将指令存入codeArray数组,指令应该从 code 中的pc开始,长度为instArray[i].len
+			codeArray = append(codeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)]...)
+			InstCodeArray = append(InstCodeArray, code[instArray[i].pc:instArray[i].pc+uint64(instArray[i].len)])
+			// 记录当前指令的地址
+			InstEndAddr = SymAddr + instArray[i].pc + uint64(instArray[i].len)
+			// 打印本次循环的具体指令和指令长度
+			fmt.Printf("inst: %s\n", instArray[i].inst.String())
+			// 打印codeArray数组的长度和内容
+			fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+			// 如果codeArray数组长度大于等于17,或者 该指令代码等于 Call、jmp、jne、je、jg、jge、jl、jle、jne、等跳转指令,跳出循环,用于跳转的指令有哪些,可以根据实际情况添加
+			if len(codeArray) >= longJumpSize {
+				fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+				// 打印InstCodeArray数组的长度和内容
+				fmt.Printf("InstCodeArray len: %d, InstCodeArray: %v\n", len(InstCodeArray), InstCodeArray)
+				break
+			}
+		}
+	}
+	// 打印最终的codeArray数组的长度和内容
+	fmt.Printf("codeArray len: %d, codeArray: %v\n", len(codeArray), codeArray)
+	// 打印最终的InstAddr地址
+	fmt.Printf("InstAddr: %x-%x  Len: %d, InstCodeArray: %v, InstCodeArray len: %d\n", InstStartAddr, InstEndAddr, len(codeArray), InstCodeArray, len(InstCodeArray))
+	instMemStruct := InstMemStruct{InstStartAddr: InstStartAddr, InstEndAddr: InstEndAddr, CodeArray: codeArray, InstCodeArray: InstCodeArray, InsertIndex: insertIndex}
+	if len(codeArray) < longJumpSize {
+		return instMemStruct, errors.New("Not enough memory for long jump")
+	}
+	return instMemStruct, nil
+}
+
+// 从 PLT 中获取函数的地址,先从改进程的 maps 中的 glibc 中获取对应的函数地址,在反过来从 plt 段中的 jmp 中计算出该函数的地址
+func getFunctionOffsetPLT(pid int, libPath, functionName string) (elf.Symbol, error) {
+	// 获取进程 maps 中 libc.so 的基地址
+	processLibcMapsInfo, err := getProcessMapsInfo(pid, "libc.so.6")
+	// 获取 libc 中 send 函数的地址
+	libcFunction, err := getFunctionOffset(processLibcMapsInfo.Path, functionName)
+	// 打印 libc 中 send 函数的地址
+	fmt.Printf("libc send function: %s, addr: %x, addr: %x\n", functionName, libcFunction.Value, libcFunction.Value+processLibcMapsInfo.Start)
+	processMapsInfo, err := getProcessMapsInfo(pid)
+	// 打印进程 maps 中的地址
+	fmt.Printf("process maps: %s, addr: %x\n", processMapsInfo.Path, processMapsInfo.Start)
+	// 解析 libPath 中的 plt 段,遍历所有的 plt 段的地址,找到 所有 jmp 指令所跳转的地址为 libc 中 send 函数的地址
+	elfFile, err := elf.Open(libPath)
+	if err != nil {
+		return elf.Symbol{}, fmt.Errorf("failed to open ELF file: %v", err)
+	}
+	defer elfFile.Close()
+	// 遍历 plt 段
+	pltSection := elfFile.Section(".plt")
+	if pltSection == nil {
+		return elf.Symbol{}, errors.New(".plt section not found")
+	}
+	pltData, err := pltSection.Data()
+	if err != nil {
+		return elf.Symbol{}, fmt.Errorf("failed to read .plt section data: %v", err)
+	}
+	entrySize := 16
+	// 计算 plt 段中一共有多少 plt 入口,使用 plt 段的大小除以 plt 入口的大小
+	// plt 入口的数量为 len(pltData)/entrySize
+	entryNum := len(pltData) / entrySize
+	baseJmpAddr := uint64(0)
+	isSetBaseJmpAddr := false
+	// 初始化一个 map,key 为指向的地址,value 为 plt 段中的地址
+	pltEntryMap := make(map[uint64]uint64)
+	// 遍历 plt 段
+	for i := 0; i < len(pltData); i += int(entrySize) {
+		// 获取 plt 段中的地址
+		addr := pltSection.Addr + uint64(i)
+		// 打印 addr
+		fmt.Printf("addr: %x\n", addr)
+		// 获取 plt 段中的 jmp 指令
+		jmpInst := pltData[i : i+int(entrySize)]
+		// 解析 jmp 指令
+		inst, err := x86asm.Decode(jmpInst, 64)
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+		// 判断 inst 是否为 jmp 指令
+		if inst.Op == x86asm.JMP {
+			jmpAddr := addr + instLen + uint64(int32(inst.Args[0].(x86asm.Mem).Disp))
+			pltEntryMap[jmpAddr] = addr
+
+			if isSetBaseJmpAddr == false {
+				// baseAddr = addr
+				baseJmpAddr = jmpAddr
+				isSetBaseJmpAddr = true
+			}
+		}
+	}
+
+	var data []byte
+	data = make([]byte, 8*(entryNum+1))
+	// 使用 ptrace attach 目标进程
+	fmt.Printf("Attach Process: %d\n", pid)
+	if err := syscall.PtraceAttach(pid); err != nil {
+		fmt.Printf("PtraceAttach Err: %v\n", err)
+	}
+	// 等待目标进程停止
+	if _, err := syscall.Wait4(pid, nil, 0, nil); err != nil {
+		fmt.Printf("wait4: %v", err)
+	}
+	dataLen, _ := syscall.PtracePeekData(pid, uintptr(baseJmpAddr+processMapsInfo.Start), data)
+
+	fmt.Printf("Ptrace DETACH: %d\n", pid)
+	// 恢复执行
+	if err = syscall.PtraceDetach(pid); err != nil {
+		fmt.Printf("ptrace DETACH: %v", err)
+	}
+
+	// 按 uint64 数组遍历 data
+	for i := 0; i < dataLen; i += 8 {
+		// 获取 data 中的地址
+		addr := *(*uint64)(unsafe.Pointer(&data[i]))
+		// 打印 data 中的地址
+		fmt.Printf("addr: 0x%x\n", addr)
+		// 如果 data 中的地址等于 libc 中 send 函数的地址,返回该地址
+		if processLibcMapsInfo.Start+libcFunction.Value == addr {
+			fmt.Printf("Found %s at address: 0x%x\n", functionName, baseJmpAddr)
+			fmt.Printf("Found %s from address: 0x%x[0x%x]\n", functionName, uint64(i)+baseJmpAddr, processMapsInfo.Start+uint64(i)+baseJmpAddr)
+			// 判断 pltEntryMap 中是否存在 baseJmpAddr,如果存在,返回 baseJmpAddr
+			if value, ok := pltEntryMap[uint64(i)+baseJmpAddr]; ok {
+				fmt.Printf("Found %s at address on plt: 0x%x\n", functionName, value)
+				return elf.Symbol{Name: functionName, Value: value, Size: 0}, nil
+			}
+		}
+	}
+	return elf.Symbol{}, fmt.Errorf("function %s not found", functionName)
+}
+
+func buildArm64Enc(imm16 uint64, hw uint64) []byte {
+	PC_START := uint64(56)
+	rd := 0x10 // x16
+	fixed := 0x1e5
+	// Combine all parts into a single 32-bit value
+	result := uint64(rd<<0) | (imm16 << 5) | (hw << 21) | uint64(fixed<<23)
+	fmt.Printf("set *(unsigned int*)($origin+%d) = 0x%08x\n", PC_START+hw*4, result)
+	return []byte{
+		byte(result),
+		byte(result >> 8),
+		byte(result >> 16),
+		byte(result >> 24),
+	}
+}
+
+// 生成一个函数,用于生成长跳指令插入,使用 x16 寄存器,使用前需要保存 x16 寄存器的值,使用后恢复 x16 寄存器的值,跳转的指令从参数传入,第二个参数为指令长度,如果指令长度不够,需要在后面添加 nop 指令
+func generateLongJumpCode(cwFunctionAddr uint64, length int, saveX16 bool) []byte {
+	jumpCode := []byte{}
+
+	if saveX16 {
+		jumpCode = append(jumpCode, 0xFF, 0x43, 0x00, 0xD1) // sub sp, sp, #16
+		jumpCode = append(jumpCode, 0xF0, 0x03, 0x00, 0xF9) // str x16, [sp]
+	}
+	jumpCode = append(jumpCode, buildArm64Enc(cwFunctionAddr&0xFFFF, 0)...)       // movz x16, (cwFunctionAddr & 0xFFFF)
+	jumpCode = append(jumpCode, buildArm64Enc((cwFunctionAddr>>16)&0xFFFF, 1)...) // movk x16, ((cwFunctionAddr >> 16) & 0xFFFF) LSL #16
+	jumpCode = append(jumpCode, buildArm64Enc((cwFunctionAddr>>32)&0xFFFF, 2)...) // movk x16, ((cwFunctionAddr >> 16) & 0xFFFF) LSL #16
+	// jumpCode = append(jumpCode, buildArm64Enc((cwFunctionAddr >> 48) & 0xFFFF, 3)...) // movk x16, ((cwFunctionAddr >> 16) & 0xFFFF) LSL #16
+	jumpCode = append(jumpCode, 0x00, 0x02, 0x1F, 0xD6) // br x16
+
+	if saveX16 {
+		jumpCode = append(jumpCode, 0xF0, 0x03, 0x40, 0xF9) // ldr x16, [sp]
+		jumpCode = append(jumpCode, 0xFF, 0x43, 0x00, 0x91) // add sp, sp, #16
+	}
+
+	// Add NOP instructions if the length is not enough
+	for len(jumpCode) < length {
+		jumpCode = append(jumpCode, 0x1F, 0x20, 0x03, 0xD5) // NOP instruction
+	}
+
+	return jumpCode
+}
+
+// 生成新指令,用于替换原指令,一个参数是原来的指令 instCodeArray,另一个参数是跳转回去的地址,第三个参数是原始 Send 函数的地址,第四个参数是在哪个指令前插入自定义指令
+func generateNewCode(instCodeArray [][]byte, jumpBackAddr uint64, sendAddr uint64, insertIndex int) ([]byte, int) {
+	newCode := []byte{}
+	hookOffset := 0
+	for i, inst := range instCodeArray {
+		if i == insertIndex {
+			/**
+			约定从栈顶拿8个字节储存 ebpf 中配置的 header 长度:先备份 x2 到 -0x08(%sp)
+			待 ebpf 将值写到 -0x08(%sp) 之后,再从 -0x08(%sp) 赋值到 x2
+			str     x2, [sp, #-8]!
+			ldr     x2, [sp], #8
+			**/
+			newCode = append(newCode, 0xE2, 0x83, 0x1F, 0xF8) // str x2, [sp, #-8]
+			hookOffset = len(newCode)
+			newCode = append(newCode, 0x1F, 0x20, 0x03, 0xD5) // NOP instruction
+			newCode = append(newCode, 0xE2, 0x83, 0x5F, 0xF8) // ldr x2, [sp, #-8]
+			/**
+			生成 Call 指令利用 x16 寄存器进程长调用调用原来的 Send 函数
+			**/
+
+			newCode = append(newCode, buildArm64Enc(sendAddr&0xFFFF, 0)...)       // movz x16, (sendAddr & 0xFFFF)
+			newCode = append(newCode, buildArm64Enc((sendAddr>>16)&0xFFFF, 1)...) // movk x16, ((sendAddr >> 16) & 0xFFFF) LSL #16
+			newCode = append(newCode, buildArm64Enc((sendAddr>>32)&0xFFFF, 2)...) // movk x16, ((sendAddr >> 16) & 0xFFFF) LSL #16
+			newCode = append(newCode, buildArm64Enc((sendAddr>>48)&0xFFFF, 3)...) // movk x16, ((sendAddr >> 16) & 0xFFFF) LSL #16
+
+			newCode = append(newCode, 0x00, 0x02, 0x3F, 0xD6) // blr x16
+			// 生成长跳指令,跳转回去
+			newCode = append(newCode, generateLongJumpCode(jumpBackAddr, longJumpSize, false)...)
+			continue
+		}
+		newCode = append(newCode, inst...)
+	}
+	return newCode, hookOffset
+}
+
+// 查找nop函数中hookOffset的位置
+func findNopFunctionHookOffset(pid int, cwSym *ProcessFunctionInfo) (int, error) {
+	// 定义一个字节数组内容:0x48, 0x8b, 0x54, 0x24, 0xf8
+	nopCode := []byte{0xE2, 0x83, 0x5F, 0xF8}
+	code, err := readMemory(pid, cwSym.Start+nopEntryOffset, cwSym.Size-nopEntryOffset)
+	if err != nil {
+		fmt.Printf("readMemory error: %v\n", err)
+		return 0, err
+	}
+	fmt.Printf("nopCode: %v\n", code)
+	pc := uint64(0)
+	for pc < uint64(len(code)) {
+		inst, err := arm64asm.Decode(code[pc:])
+		if err != nil {
+			fmt.Printf("Decode error: %v\n", err)
+			break
+		}
+		// 打印对应指令的字节码
+		fmt.Printf("%s[%d]-[%v]\n", inst.String(), pc, code[pc:pc+instLen])
+		// 如果指令的字节码等于nopCode,返回当前的pc
+		if string(code[pc:pc+instLen]) == string(nopCode) {
+			return int(pc) - nopLen, nil
+		}
+		pc += instLen
+	}
+	return 0, errors.New("hookOffset not found")
+}

+ 11 - 6
run.sh

@@ -1,7 +1,7 @@
 #!/bin/sh
 # pid=`ps aux | grep ebpfdemo81 | grep -v grep | awk '{print $2}'`
 # echo $pid
-# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=go SEND=1 FILTER_PID=$pid WHITE_LIST=".*HandleFunc|.*main.*|.*serverHandler.*|.*ServeHTTP.*" ./euspace --listen="0.0.0.0:8123"
+# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=go SEND=1 FILTER_PID=$pid WHITE_LIST=".*HandleFunc|.*main.*|.*serverHandler.*|.*ServeHTTP.*" ./euspace --listen="0.0.0.0:8124"
 
 
 # pid=`ps aux | grep ./helloworld | grep -v grep | awk '{print $2}'`
@@ -13,11 +13,16 @@
 # echo $pid
 # TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=go DBG_PATH="" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"
 
-# pid=`ps aux | grep CoreAoT | grep -v grep | awk '{print $2}'`
+pid=`ps aux | grep CoreAoT | grep -v grep | awk '{print $2}'`
+echo $pid
+CONFIG_SERVER=http://10.0.7.115:18080 DATA_SERVER=http://10.0.7.115:18080 TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=dotnet DBG_PATH="/data/roger/ebpfdemo/net8_demo/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT.dbg" SEND=1 FILTER_PID=$(pidof CoreAoT) WHITE_LIST="main.*|Addwj.*|CoreAoT_Program___Main__.*|*SocketConnection__Start*" ./euspace  --listen="0.0.0.0:8124"
+
+
+# pid=`ps aux | grep SimpleHttpServer | grep -v grep | awk '{print $2}'`
 # echo $pid
-# TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=dotnet DBG_PATH="/data/NET8/CoreAoT/bin/Debug/net8.0/linux-x64/publish/CoreAoT.dbg" SEND=1 FILTER_PID=$(pidof CoreAoT) WHITE_LIST="main.*|Addwj.*|CoreAoT_Program___Main__.*|*SocketConnection__Start*" ./euspace  --listen="0.0.0.0:8123"
+# CONFIG_SERVER=http://10.0.7.115:18080 DATA_SERVER=http://10.0.7.115:18080 TRACES_ENDPOINT=http://10.0.6.103:8099/docp/api/v2/data/receive BIN_TYPE=java DBG_PATH="/data/roger/ebpfdemo/graalve_demo/SimpleHttpServer.dbg" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"
 
 
-pid=`pidof java`
-echo $pid
-TRACES_ENDPOINT=http://10.0.12.192:18080/docp/api/v2/data/receive BIN_TYPE=java DBG_PATH="" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"
+# pid=`pidof java`
+# echo $pid
+# TRACES_ENDPOINT=http://10.0.12.192:18080/docp/api/v2/data/receive BIN_TYPE=java DBG_PATH="" SEND=1 FILTER_PID=$pid WHITE_LIST="handle*|addw.*" ./euspace  --listen="0.0.0.0:8124"