参考stackplz实现对syscall进行栈回溯的时候不知道为什么回溯出来的堆栈不完整,但是成功回溯的部分都是准确的,具体情况如下


第一个是期望的结果,是stackplz生成的,下面那个是实际结果,检查过sp,pc的值也是对的,这里两个例子不是同一处函数调用
代码贴这里,采集栈的时候试了很多种办法,试过bpf_get_stack和直接用bpf_read_user从sp开始读16kb的栈数据,但是结果都是一样的,栈都是只能回溯2到3层,不清楚是采集的问题还是unwindstack设置错了,如果是采集的问题有没有不用patch libbpf后用perf_event_submit的方法,主要是不是很想用perf_buffer传递数据
//+build ignore
#include "common.h"
#include "vmlinux.h"
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
const struct sysEnterData *unusedsysEnterData __attribute__((unused));
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, PAGE_SIZE * 65536);
} sysEnterRb SEC(".maps");
static void bufRead(struct sysEnterData *data) {
switch (data->syscall_id) {
case OPENAT:
case READ:
case WRITE:
case PREAD64:
case PWRITE64:
case NEWFSTATAT:
case EXECVEAT:
case OPENAT2:
case READLINKAT:
bpf_probe_read_user_str(data->argBuf[0], sizeof(data->argBuf[0]),
(void *)data->regs[1]);
break;
case EXECVE:
bpf_probe_read_user_str(data->argBuf[0], sizeof(data->argBuf[0]),
(void *)data->regs[0]);
break;
default:
break;
}
}
#define MAX_MEMORY_RANGE 128
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, MAX_MEMORY_RANGE);
__type(key, u32);
__type(value, struct memoryRange);
} targetMemoryRange SEC(".maps");
struct memoryCheckCtx {
bool found;
int pc;
};
static int memory_check(u64 idx, void *ctx) {
struct memoryCheckCtx *checkCtx = ctx;
struct memoryRange *range = bpf_map_lookup_elem(&targetMemoryRange, &idx);
if (range == NULL || (range->start == 0)) {
return 1; // skip empty or invalid ranges
}
if (checkCtx->pc >= range->start && checkCtx->pc <= range->end) {
checkCtx->found = true;
return 1; // stop iterating
}
return 0; // continue iterating
}
bool filter_MemoryRange(uint64_t pc) {
struct memoryCheckCtx checkCtx = {.found = false, .pc = pc};
bpf_loop(MAX_MEMORY_RANGE, memory_check, &checkCtx, 0);
return checkCtx.found;
}
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 0x1c3);
__type(key, u32);
__type(value, u8);
} targetSyscalls SEC(".maps");
bool filter_syscall(long id) {
bool *isTarget = bpf_map_lookup_elem(&targetSyscalls, &id);
if (isTarget == NULL) {
return false;
}
return *isTarget;
}
SEC("tp_btf/sys_enter")
int BPF_PROG(sys_enter, struct pt_regs *regs, long id) {
// bpf_printk("pid : %d target : %d \n", (bpf_get_current_pid_tgid() >> 32),
// targetPid);
if ((bpf_get_current_pid_tgid() >> 32) != targetPid) {
return 0;
}
if (!filter_syscall(id))
return 0;
// if (!filter_MemoryRange(regs->pc))
// return 0;
bpf_printk("pid: %d nr: %d \n", bpf_get_current_pid_tgid() >> 32, id);
// prepare the stack data
struct sysEnterData *data =
bpf_ringbuf_reserve(&sysEnterRb, sizeof(struct sysEnterData), 0);
if (data == NULL) {
bpf_printk("Failed to reserve space in ring buffer\n");
return 0;
}
data->pc = regs->pc;
data->sp = regs->sp;
for (int i = 0; i < 31; i++) {
data->regs[i] = regs->regs[i];
}
bpf_probe_read_user(&data->stackData, sizeof(data->stackData),
(void *)regs->sp);
data->stackSize = 16384;
bpf_get_current_comm(data->comm, sizeof(data->comm));
data->syscall_id = id;
bufRead(data);
bpf_ringbuf_submit(data, 0);
return 0;
}#include "stackHelp.h"
#include "unwindstack/Regs.h"
#include "unwindstack/UserArm64.h"
#include <cstddef>
#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <stdio.h>
#include <string>
#include <sys/resource.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm64.h>
#include <unwindstack/Unwinder.h>
struct Data {
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
char stackData[16384];
uint64_t stackSize;
};
unwindstack::Regs *prepareReg(Data *data) {
unwindstack::arm64_user_regs arm64_user_regs;
memset(&arm64_user_regs, 0, sizeof(arm64_user_regs));
memcpy(&arm64_user_regs.regs, &data->regs, sizeof(data->regs));
arm64_user_regs.sp = data->sp;
arm64_user_regs.pc = data->pc;
auto regs = static_cast<unwindstack::RegsArm64 *>(
unwindstack::RegsArm64::Read(&arm64_user_regs));
regs->SetPACMask(0);
return regs;
}
std::string readFileToString(const std::string &filePath) {
std::ifstream inputFile(filePath);
if (!inputFile.is_open()) {
return ""; // 返回空字符串表示失败。
}
// 3. 创建一个字符串流(stringstream)对象。
std::stringstream buffer;
buffer << inputFile.rdbuf();
return buffer.str();
}
extern "C" {
void unwind(int pid, Data *data) {
std::unique_ptr<unwindstack::Regs> unwind_regs(prepareReg(data));
if (unwind_regs == NULL) {
fprintf(stderr, "Failed to prepare registers\n");
return;
}
std::shared_ptr<unwindstack::Memory> stack =
unwindstack::Memory::CreateOfflineMemory(
reinterpret_cast<uint8_t *>(data->stackData), data->sp,
data->sp + data->stackSize);
std::string mapsBuffer;
std::unique_ptr<unwindstack::Maps> maps;
std::string mapsPath = "/proc/" + std::to_string(pid) + "/maps";
mapsBuffer = readFileToString(mapsPath);
if (mapsBuffer == "") {
fprintf(stderr, "Failed to read maps file: %s\n", mapsPath.c_str());
return;
}
maps.reset(new unwindstack::BufferMaps(mapsBuffer.c_str()));
maps->Parse();
unwindstack::Unwinder unwinder(512, maps.get(), unwind_regs.get(), stack);
unwinder.Unwind();
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
printf("%s\n", unwinder.FormatFrame(i).c_str());
}
}
void test_CGO(int a) { std::cout << "test : " << a << "\n"; }
}package stackunwinder
// #include "../linker/wrapper.h"
import "C"
import (
"bytes"
"encoding/binary"
"flag"
"log"
"os"
"path"
"strings"
"unsafe"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/ringbuf"
"github.com/cilium/ebpf/rlimit"
)
func initLibs() {
exe, _ := os.Executable()
exeDir := path.Dir(exe)
C.setupLibEnv(C.CString(exeDir)) // initialize the C library
if isDebug {
C.test_CGO(12345)
}
}
var ProbeObjs *probes_Objects = nil
func Main() {
var (
targetPid = flag.Uint("pid", 0, "target pid")
_isDebug = flag.Bool("d", false, "enable debug mode")
targetSyscall = flag.String("s", "", "target syscall name,splitted by ',' ")
)
flag.Parse()
setDebugMode(*_isDebug)
initLibs()
debug("target pid %d \n", *targetPid)
if err := rlimit.RemoveMemlock(); err != nil { // remove kernel memory lock limit
log.Fatal(err)
}
var opts *ebpf.CollectionOptions
if isDebug {
opts = &ebpf.CollectionOptions{
Programs: ebpf.ProgramOptions{
LogLevel: ebpf.LogLevelInstruction,
LogSizeStart: 1024 * 1024, // 1MB log size
},
}
} else {
opts = nil
}
objs := probes_Objects{}
if err := loadProbes_Objects(&objs, opts); err != nil {
log.Fatalf("loading objects: %v", err)
}
defer objs.Close()
ProbeObjs = &objs // save the probes objects globally
objs.TargetPid.Set(uint32(*targetPid)) // set target pid in eBPF program
setTargetSyscall(strings.Split(*targetSyscall, ","))
debug("self pid: %d\n", uint32(os.Getpid()))
debug("bpf obj loaded\n")
sysEnterTp, err := link.AttachTracing(link.TracingOptions{Program: objs.SysEnter, AttachType: ebpf.AttachTraceRawTp}) // attach to sys_enter
if err != nil {
log.Fatal(err)
}
defer sysEnterTp.Close()
if isDebug {
log.Printf("tp attached\n")
}
sysEnterRb, err := ringbuf.NewReader(objs.SysEnterRb)
if err != nil {
log.Fatal()
}
defer sysEnterRb.Close()
debug("sysEnter ringbuf reader created\n")
var sysEnterData probes_SysEnterData
for {
data, err := sysEnterRb.Read()
if err != nil {
log.Printf("reading err: %v", err)
continue
}
if err := binary.Read(bytes.NewBuffer(data.RawSample), binary.LittleEndian, &sysEnterData); err != nil {
log.Printf("reading event err: %v", err)
continue
}
// log.Printf("pid: %d nr: %d\n", sysEnterData.Pid, sysEnterData.SyscallId)
PrintSyscallInfo(&sysEnterData)
data, err = sysEnterRb.Read()
if err != nil {
log.Printf("reading sysEnter err: %v", err)
continue
}
if err := binary.Read(bytes.NewBuffer(data.RawSample), binary.LittleEndian, &sysEnterData); err != nil {
log.Printf("reading sysEnterData err: %v", err)
continue
}
debug("stacksize %d\n", sysEnterData.StackSize)
debug("pc %x\n", sysEnterData.Pc)
debug("sp %x\n", sysEnterData.Sp)
var tmp C.struct_Data
for i := 0; i < 31; i++ {
tmp.regs[i] = C.uint64_t(sysEnterData.Regs[i])
}
tmp.pc = C.uint64_t(sysEnterData.Pc)
tmp.sp = C.uint64_t(sysEnterData.Sp)
for i := 0; i < int(sysEnterData.StackSize); i++ {
tmp.stackData[i] = C.char(sysEnterData.StackData[i])
}
tmp.stackSize = C.uint64_t(sysEnterData.StackSize)
C.unwind(C.int(*targetPid), (*C.struct_Data)(unsafe.Pointer(&tmp)))
}
}
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2025-8-20 17:43
被SGSGsama编辑
,原因: