首页
社区
课程
招聘
未解决 ebpf/unwindstack栈回溯不完整 10雪币
发表于: 2025-8-20 16:56 840

未解决 ebpf/unwindstack栈回溯不完整 10雪币

2025-8-20 16:56
840

参考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)))
    }
}



传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-8-20 17:43 被SGSGsama编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 104
活跃值: (7486)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql
2025-8-21 16:02
0
雪    币: 510
活跃值: (2102)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
577K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6e0k6h3g2r3L8r3!0%4k6i4u0j5i4K6u0r3k6h3u0H3k6W2)9J5c8X3u0D9L8$3u0Q4x3V1j5$3x3h3y4T1j5K6c8S2x3K6N6W2z5o6t1$3y4r3x3K6y4K6R3^5y4o6V1@1j5e0M7@1k6r3y4W2j5K6V1&6x3X3x3$3x3K6k6T1z5e0p5^5i4K6u0r3M7r3g2J5k6W2)9J5c8Y4u0A6L8X3N6Q4x3X3g2Y4L8#2)9J5x3@1H3I4y4e0t1`.

两种采集方式有差异,正是因为基于stack frame的效果不好,才用的dwarf的模式

可以阅读这位作者写的文章 https://bbs.kanxue.com/thread-274546.htm
2025-11-26 14:27
0
游客
登录 | 注册 方可回帖
返回