-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathmain.cpp
More file actions
310 lines (259 loc) · 11.6 KB
/
Copy pathmain.cpp
File metadata and controls
310 lines (259 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include "memtool/membase.hpp"
#include "memtool/memextend.hpp"
#include "chainer/ccscan.hpp" // IWYU pragma: keep
#include "chainer/ccompare.hpp" // IWYU pragma: keep
#include "utils/cmd_parser.h"
#include <cstdint>
#include <cstdio>
#include <sstream>
using namespace utils;
namespace {
std::string format_chain_line(const std::string &module_name, int module_index,
const std::vector<size_t> &offsets) {
std::ostringstream oss;
oss << module_name << "[" << module_index << "]";
if (!offsets.empty()) {
oss << std::hex << std::uppercase;
for (size_t i = 0; i < offsets.size(); ++i) {
oss << (i == 0 ? " + 0x" : " -> + 0x") << offsets[i];
}
oss << std::nouppercase << std::dec;
}
return oss.str();
}
} // namespace
int main(int argc, char *argv[]) {
// 创建命令行解析器
CommandLineParser parser("newscan", "高性能内存指针链分析工具");
// 添加命令行选项
parser.addOption({'p', "process", "目标进程名称或PID", true, false});
parser.addOption({'a', "address", "目标地址(16进制,不带0x前缀)", true, false});
parser.addOption({'d', "depth", "最大搜索深度", true, false, "10"});
parser.addOption({'o', "offset", "最大偏移量(16进制,不带0x前缀)", true, false, "500"});
parser.addOption({'l', "limit", "结果限制数量", true, false, "0"});
parser.addOption({'f', "file", "输出文件名", true, false, "pointer_chains.txt"});
parser.addOption({0, "compare-bin", "比较两份指针链二进制文件", false, false});
parser.addOption({0, "compare-txt", "比较两份指针链文本文件", false, false});
parser.addOption({0, "lhs", "旧版指针链二进制文件路径", true, false});
parser.addOption({0, "rhs", "新版指针链二进制文件路径", true, false});
parser.addOption({0, "report", "对比输出文件名", true, false,
"chain_compare.txt"});
parser.addOption({'v', "verbose", "详细输出模式", false, false});
parser.addOption({0, "io", "使用/proc/pid/mem IO读取内存(解决不可读内存问题)", false, false});
parser.addOption({0, "path", "路径扫描模式:查找两个地址之间的指针路径", false, false});
parser.addOption({0, "from", "路径扫描的源地址(16进制)", true, false});
parser.addOption({0, "to", "路径扫描的目标地址(16进制)", true, false});
parser.addOption({'h', "help", "显示帮助信息", false, false});
// 设置用法说明
parser.setUsage("[扫描] -p <进程名/PID> -a <地址> | "
"[路径] -p <进程名/PID> --path --from <源地址> --to <目标地址> | "
"[对比] (--compare-bin|--compare-txt) --lhs <旧文件> --rhs <新文件>");
// 解析命令行参数
if (!parser.parse(argc, argv)) {
std::cerr << "错误: " << parser.getErrorMessage() << std::endl;
parser.showHelp();
return 1;
}
// 如果请求帮助,显示帮助信息并退出
if (parser.hasOption("help")) {
parser.showHelp();
return 0;
}
bool compare_bin_mode = parser.getBoolOption("compare-bin", false);
bool compare_txt_mode = parser.getBoolOption("compare-txt", false);
if (compare_bin_mode || compare_txt_mode) {
if (!parser.hasOption("lhs") || !parser.hasOption("rhs")) {
std::cerr << "错误: 对比模式需要提供 --lhs 与 --rhs 选项" << std::endl;
return 1;
}
auto lhs_path = parser.getOptionValue("lhs");
auto rhs_path = parser.getOptionValue("rhs");
std::string report_path =
parser.getOptionValue("report", "chain_compare.txt");
FILE *report = fopen(report_path.c_str(), "w");
if (report == nullptr) {
std::cerr << "警告: 无法创建报告文件: " << report_path << std::endl;
}
chainer::ccompare<size_t> comparer;
chainer::bin_compare_result<size_t> compare_result;
try {
if (compare_bin_mode) {
// 二进制对比:匹配链直接流式写入 report,不存储在内存中
if (report != nullptr) {
fprintf(report, "=== 指针链二进制文件对比结果 ===\n\n");
}
compare_result = comparer.compare_bin_files(lhs_path, rhs_path, report);
} else {
compare_result = comparer.compare_txt_files(lhs_path, rhs_path);
}
} catch (const std::exception &ex) {
if (report != nullptr) fclose(report);
std::cerr << "错误: " << ex.what() << std::endl;
return 1;
}
// 写入统计摘要
if (report != nullptr) {
if (compare_txt_mode) {
fprintf(report, "=== 指针链文本文件对比结果 ===\n\n");
}
// 二进制模式下链详情已在比较过程中写入,这里追加统计
fprintf(report, "--- 统计 ---\n");
fprintf(report, "旧文件链数量: %zu\n", compare_result.lhs_total);
fprintf(report, "新文件链数量: %zu\n", compare_result.rhs_total);
fprintf(report, "保持不变链数量: %zu\n", compare_result.unchanged);
fprintf(report, "\n");
if (compare_txt_mode) {
// 文本对比:链存储在内存中,在此输出
if (compare_result.modules.empty()) {
fprintf(report, "未找到共同存在的指针链。\n");
} else {
for (const auto &diff : compare_result.modules) {
fprintf(report, "模块: %s[%d]\n", diff.module_name.c_str(),
diff.module_index);
if (!diff.common.empty()) {
fprintf(report, " 保持不变的链:\n");
for (const auto &chain : diff.common) {
fprintf(report, " = %s\n",
format_chain_line(diff.module_name, diff.module_index,
chain).c_str());
}
}
fprintf(report, "\n");
}
}
}
fclose(report);
printf("对比报告已保存至: %s\n", report_path.c_str());
}
return 0;
}
// ============ 路径扫描模式 ============
bool path_mode = parser.getBoolOption("path", false);
if (path_mode) {
if (!parser.hasOption("process")) {
std::cerr << "错误: 路径扫描模式需要提供目标进程 (-p/--process)" << std::endl;
return 1;
}
if (!parser.hasOption("from") || !parser.hasOption("to")) {
std::cerr << "错误: 路径扫描模式需要提供 --from 和 --to 地址" << std::endl;
return 1;
}
std::string target_process = parser.getOptionValue("process");
int target_pid = -1;
try {
target_pid = std::stoi(target_process);
} catch (...) {
target_pid = memtool::base::get_pid(target_process.c_str());
if (target_pid == -1) {
std::cerr << "错误: 无法找到进程: " << target_process << std::endl;
return 1;
}
}
printf("Target PID: %s -> %d\n", target_process.c_str(), target_pid);
uint32_t max_depth = parser.getIntOption("depth", 5);
uint32_t max_offset = static_cast<uint32_t>(
std::stoul(parser.getOptionValue("offset", "500"), nullptr, 16));
memtool::base::target_pid = target_pid;
if (parser.getBoolOption("io", false)) {
memtool::base::mode = memtool::read_mode::PROC_MEM_IO;
printf("使用 /proc/%d/mem IO 读取模式\n", target_pid);
}
chainer::cscan<size_t> scanner;
memtool::extend::get_target_mem();
memtool::extend::set_mem_ranges(memtool::Anonymous + memtool::C_alloc +
memtool::C_bss + memtool::C_data);
size_t pointer_count = scanner.get_pointers(0, 0, false, 10, 1 << 20);
printf("Found %ld potential pointers\n", pointer_count);
uint64_t from_addr = std::stoull(parser.getOptionValue("from"), nullptr, 16);
uint64_t to_addr = std::stoull(parser.getOptionValue("to"), nullptr, 16);
std::string output_file = parser.getOptionValue("file", "pointer_paths.txt");
FILE *output = fopen(output_file.c_str(), "w");
if (output == nullptr) {
std::cerr << "错误: 无法创建输出文件: " << output_file << std::endl;
return 1;
}
size_t path_count = scanner.scan_pointer_path(
static_cast<size_t>(from_addr), static_cast<size_t>(to_addr),
max_depth, max_offset, output);
printf("Total paths found: %ld\n", path_count);
fclose(output);
return 0;
}
if (!parser.hasOption("process")) {
std::cerr << "错误: 扫描模式需要提供目标进程 (-p/--process)" << std::endl;
return 1;
}
if (!parser.hasOption("address")) {
std::cerr << "错误: 扫描模式需要提供目标地址 (-a/--address)" << std::endl;
return 1;
}
// 第一步:获取目标进程
std::string target_process = parser.getOptionValue("process");
int target_pid = -1;
// 尝试解析进程ID
try {
target_pid = std::stoi(target_process);
} catch (...) {
// 不是数字,认为是进程名
target_pid = memtool::base::get_pid(target_process.c_str());
if (target_pid == -1) {
std::cerr << "错误: 无法找到进程: " << target_process << std::endl;
return 1;
}
}
printf("Target PID: %s -> %d\n", target_process.c_str(), target_pid);
// 获取命令行参数
uint32_t max_depth = parser.getIntOption("depth", 5);
uint32_t max_offset = static_cast<uint32_t>(std::stoul(parser.getOptionValue("offset", "500"), nullptr, 16));
// 第二步:初始化扫描器
memtool::base::target_pid = target_pid;
// 设置内存读取模式
if (parser.getBoolOption("io", false)) {
memtool::base::mode = memtool::read_mode::PROC_MEM_IO;
printf("使用 /proc/%d/mem IO 读取模式\n", target_pid);
}
chainer::cscan<size_t> scanner; // 64位进程,32位使用 uint32_t
// 第三步:获取目标进程内存布局
memtool::extend::get_target_mem();
memtool::extend::set_mem_ranges(memtool::Anonymous + memtool::C_alloc +
memtool::C_bss + memtool::C_data);
// 第四步:扫描潜在指针
auto start_time = std::chrono::high_resolution_clock::now();
// 参数:起始地址=0, 结束地址=0(不限制), 全扫描=false, 缓冲区数=10, 缓冲区大小=1MB
size_t pointer_count = scanner.get_pointers(0, 0, false, 10, 1 << 20);
printf("Found %ld potential pointers\n", pointer_count);
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
printf("Scanning time: %lld milliseconds\n", static_cast<long long>(duration.count()));
// 第五步:构建指针链
uint64_t target_addr = std::stoull(parser.getOptionValue("address"), nullptr, 16);
std::vector<size_t> target_addrs;
target_addrs.emplace_back(target_addr);
// 直接输出文本格式(推荐方式,避免中间二进制文件转换)
std::string output_file = parser.getOptionValue("file", "pointer_chains.txt");
FILE *output = fopen(output_file.c_str(), "w+");
if (output == nullptr) {
std::cerr << "错误: 无法创建输出文件: " << output_file << std::endl;
return 1;
}
size_t chain_count = scanner.scan_pointer_chain_to_txt(
target_addrs, max_depth, max_offset, false, 0, output);
printf("Total pointer chains found: %ld\n", chain_count);
fclose(output);
/* 方式2: 原始二进制格式(需要二次转换)
auto f = fopen("1", "wb+");
auto chaincount =
t.scan_pointer_chain(addrs, maxDepth, maxOffset, false, 0, f);
printf("chaincount %ld\n", chaincount); // 10层 偏移500
fclose(f);
// 格式化输出
chainer::cformat<size_t> t2;
auto f2 = fopen("1", "rb+");
printf("%ld\n", t2.format_bin_chain_data(f2, "2", false)); // 文件
// printf("%ld\n", t2.format_bin_chain_data(f2, "2", true)); // 文件夹
// 需要在当前目录有2文件夹
fclose(f2);
*/
return 0;
}