| 98 | } // anonymous |
| 99 | |
| 100 | NodePtr build_proc_tree(const std::vector<linux::Process>& procs, |
| 101 | const PhysicalLayer& phys, |
| 102 | const x86_64::PageTable& /*kern_pt*/, |
| 103 | const IsfSymbols& isf, |
| 104 | const linux::KernelContext& kctx, |
| 105 | const Engine& eng) |
| 106 | { |
| 107 | auto root = std::make_shared<DirNode>("proc"); |
| 108 | |
| 109 | for (const auto& p : procs) { |
| 110 | auto ctx = std::make_shared<ProcCtx>(ProcCtx{ &phys, &isf, &kctx, &eng, p }); |
| 111 | auto folder_name = fmt::format("{}-{}", p.pid, sanitize(p.comm)); |
| 112 | auto dir = std::make_shared<DirNode>(folder_name); |
| 113 | |
| 114 | // Forensic warming is scoped to real user processes. Kernel threads |
| 115 | // (mm == 0) have no address space / open files, so warming their |
| 116 | // per-task files is mostly pointless work — tag those as cheap so the |
| 117 | // warmer skips them. Files that only exist when mm != 0 (malfind, |
| 118 | // entropy, yara, …) already can't appear for kernel threads. |
| 119 | const FileCost procWarm = (p.mm != 0) ? kPerProc : FileCost{}; |
| 120 | |
| 121 | // info.txt — populated lazily so VMA enumeration only happens on first read. |
| 122 | dir->add(std::make_shared<LazyFileNode>("info.txt", [ctx]() { |
| 123 | std::size_t vma_count = 0; |
| 124 | u64 user_bytes = 0; |
| 125 | if (ctx->p.mm) { |
| 126 | auto vmas = linux::enumerate_vmas(*ctx->phys, *ctx->isf, *ctx->kctx, ctx->p); |
| 127 | vma_count = vmas.size(); |
| 128 | for (auto& v : vmas) user_bytes += v.size(); |
| 129 | } |
| 130 | return make_info_txt(ctx->p, vma_count, user_bytes); |
| 131 | })); |
| 132 | |
| 133 | // memmap.txt — our pretty VMA table (kept for human-readable view). |
| 134 | dir->add(std::make_shared<LazyFileNode>("memmap.txt", [ctx]() { |
| 135 | if (!ctx->p.mm) return ByteBuf{}; |
| 136 | auto vmas = linux::enumerate_vmas(*ctx->phys, *ctx->isf, *ctx->kctx, ctx->p); |
| 137 | return make_memmap_txt(vmas); |
| 138 | })); |
| 139 | |
| 140 | // ----- Per-process Linux-compatible files. Each one is lazy; they only touch |
| 141 | // memory on first read. |
| 142 | dir->add(std::make_shared<LazyFileNode>("cmdline", [ctx]() { |
| 143 | return linux::gen_cmdline(*ctx->phys, *ctx->isf, *ctx->kctx, ctx->p); |
| 144 | })); |
| 145 | auto environ_node = std::make_shared<LazyFileNode>("environ", [ctx]() { |
| 146 | return linux::gen_environ(*ctx->phys, *ctx->isf, *ctx->kctx, ctx->p); |
| 147 | }); |
| 148 | dir->add(environ_node); |
| 149 | dir->add(std::make_shared<LazyFileNode>("comm", [ctx]() { |
| 150 | return linux::gen_comm(ctx->p); |
| 151 | })); |
| 152 | dir->add(std::make_shared<LazyFileNode>("maps", [ctx]() { |
| 153 | if (!ctx->p.mm) return ByteBuf{}; |
| 154 | auto vmas = linux::enumerate_vmas(*ctx->phys, *ctx->isf, *ctx->kctx, ctx->p); |
| 155 | return linux::gen_maps(vmas); |
| 156 | })); |
| 157 | dir->add(std::make_shared<LazyFileNode>("status", [ctx]() { |
no test coverage detected