摘要(CSDN摘要框粘贴,120字左右)
常规STL迭代器只能顺序单向遍历,无法中途暂停、定点重放。本文从Burp Suite Proxy/Repeater功能获取灵感,使用C++模板封装自定义迭代器,实现Intercept拦截暂停、forward放行、双版本repeater定点重放(只读/可修改索引),附带forEach可选重置参数,附带多组实测案例,剖析状态机+索引管控设计思路。
正文
一、前言:为什么要自研这款迭代器?
日常开发中,std::vector原生迭代器、范围for循环都是一次性顺序遍历,走到头就结束,无法:
- 遍历中途暂停卡住(类似Burp抓包拦截)
- 暂停后定点重复执行当前元素(Burp Repeater重放发包)
- 手动修改游标索引,实现前进、后退、跳转起点
forEach遍历可选从头开始/从断点续跑
本人从Burp Suite的Intercept(拦截)、Forward(放行)、Repeater(重放)三个经典功能得到启发,从零封装模板迭代器,用C++实现一套带状态控制的自定义迭代器框架。
功能对标Burp对照表
| 自定义迭代器接口 | 对应Burp功能 | 功能说明 |
|---|---|---|
| Intercept() | Proxy拦截 | 锁定当前索引,停止游标自增,暂停遍历 |
| forward() | Proxy放行 | 解除拦截锁,恢复正常索引自增 |
| repeater(只读lambda) | Repeater | 拦截状态下重复读取当前元素,不修改索引 |
| repeater(带索引引用lambda) | Repeater高级 | 可直接修改迭代索引,实现回退/跳转/无限循环 |
| forEach(fn, RESET) | 遍历控制器 | RESET=true每次遍历重置索引;false从上一次断点继续遍历 |
二、完整头文件 Iterator.h 源码
cpp
#ifndef ITERATOR_H
#define ITERATOR_H
#include<cstddef>
using time_limitation = unsigned __int64;
#include <vector>
#include <functional>
#include <stdexcept>
#include <initializer_list>
#include <stack>
#include <algorithm>
template <typename T>
class Iterator {
private:
std::vector<T> vec;
size_t index = 0;
bool isIntercept = false;
T lanjieyuansu = T();
// 核心解耦:索引自增逻辑私有封装,拦截状态禁止索引++
void incrementIndex() {
if (!isIntercept) {
index++;
}
}
public:
// 多构造函数:vector/初始化列表/stack三种容器快速构造
Iterator(std::vector<T> v = {}) : vec(std::move(v)), index(0) {}
Iterator(std::initializer_list<T> list) : vec(list), index(0) {}
Iterator(const std::stack<T>& stk) {
std::stack<T> temp_stk = stk;
vec.clear();
while (!temp_stk.empty()) {
vec.push_back(temp_stk.top());
temp_stk.pop();
}
std::reverse(vec.begin(), vec.end());
index = 0;
}
~Iterator() = default;
// 拷贝构造、重载赋值运算符
Iterator(const Iterator& other) : vec(other.vec), index(other.index), isIntercept(other.isIntercept) {}
Iterator& operator=(const Iterator& other) {
if (this != &other) {
vec = other.vec;
index = other.index;
isIntercept = other.isIntercept;
}
return *this;
}
Iterator& operator=(std::initializer_list<T> list) {
vec = list;
index = 0;
return *this;
}
Iterator& operator=(const std::stack<T>& stk) {
std::stack<T> temp_stk = stk;
vec.clear();
while (!temp_stk.empty()) {
vec.push_back(temp_stk.top());
temp_stk.pop();
}
std::reverse(vec.begin(), vec.end());
index = 0;
return *this;
}
// 动态追加元素
Iterator& add(const T& value) {
vec.push_back(value);
return *this;
}
// 手动重置游标到起始位置
Iterator& reset() {
index = 0;
return *this;
}
// 判断是否存在下一元素
bool hasNext() const {
return index < vec.size();
}
// 取出当前元素并根据拦截状态决定索引自增
T next() {
if (!hasNext()) {
throw std::out_of_range("Iterator out of range");
}
T val = vec[index];
incrementIndex();
return val;
}
/**
* @brief 批量遍历容器
* @param RESET true:每次遍历重置索引;false:从上一次停留位置继续遍历
*/
inline Iterator& forEach(std::function<void(T)> fn, bool RESET = true) {
if (RESET) {
reset();
}
while (hasNext()) {
fn(next());
}
return *this;
}
// 拦截:锁定当前游标,停止索引自增
void Intercept() {
lanjieyuansu = vec[index];
isIntercept = true;
}
// 放行:取消拦截锁定
void forword() {
isIntercept = false;
}
// Repeater重载1:只读重放,无法修改索引
void repeater(std::function<void(T)> fn) {
if (!isIntercept) {
throw std::logic_error("Not in intercept mode");
}
fn(vec.at(index));
}
// Repeater重载2:高危版,引用传递索引,可外部修改游标
void repeater(std::function<void(T, size_t&)> fn) {
std::cout << "拦截修改索引有风险!!!" << std::endl;
if (!isIntercept) {
throw std::logic_error("Not in intercept mode");
}
fn(vec.at(index), index);
}
// 获取底层容器const引用
const std::vector<T>& getVector() const {
return vec;
}
};
#endif
三、接口核心设计思路拆解
3.1 拦截/放行状态机原理
用成员变量bool isIntercept做状态标记:
Intercept():标记拦截,保存当前元素,incrementIndex()内部判断拦截为true时不再执行index++,游标卡死在当前位置;forward():取消标记,后续next()正常自增索引,继续向后遍历。
关键设计:索引自增逻辑
incrementIndex私有封装,外部无法直接修改,只有repeater高危重载主动透出索引引用。
3.2 双版本Repeater重放设计
- 只读重载
reater(function<void(T)>):仅传入当前元素值,安全重复调用,和Burp普通Repeater一致,反复发送当前数据包; - 高危重载
repeater(function<void(T,size_t&)>):索引以引用传入lambda,外部可自由y=0回到起点、y--向前回退、y++向后跳转,可控时空遍历,但无符号size_t越减溢出(0-1=超大无符号数)会触发越界异常。
3.3 forEach的RESET参数妙用
- 默认
RESET=true:每次调用forEach自动reset(),游标归零从头遍历; RESET=false:保留上次遍历停下的index,断点续跑,单次调用看不出差异,嵌套循环多次调用时效果明显。
四、多组实战测试Demo
Demo1:常规拦截+放行+定点多次重放
cpp
#include<iostream>
#include"../Iterator.h"
int main() {
try {
std::vector<int> vec = {10, 20, 30};
Iterator it = vec;
it.forEach([&](int x) {
std::cout << x << std::endl;
if (x == 20) {
it.Intercept();
std::cout << "拦截中!" << std::endl;
// 循环4次只读重放
for (int i = 0; i <= 3; i++) {
it.repeater([ = ](int x) {
std::cout << "当前拦截:" << x << std::endl;
});
}
it.forword();
std::cout << "放行!!" << std::endl;
}
getwchar();
});
} catch (const std::out_of_range& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
运行输出:
10
20
拦截中!
当前拦截:20
当前拦截:20
当前拦截:20
当前拦截:20
放行!!
30
Demo2:高危重放修改索引y=0,遍历从头无限循环
cpp
#include<iostream>
#include"../Iterator.h"
int main() {
try {
std::vector<int> vec = {10, 20, 30};
Iterator it = vec;
it.forEach([&](int x) {
std::cout << x << std::endl;
if (x == 20) {
it.Intercept();
std::cout << "拦截中!" << std::endl;
for (int i = 0; i <= 3; i++) {
it.repeater([ = ](int x, size_t& y) {
y = 0; // 索引强制归零
std::cout << "当前拦截:" << x << std::endl;
});
}
it.forword();
std::cout << "放行!!" << std::endl;
}
getwchar();
});
} catch (const std::out_of_range& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
现象:放行后index=0,迭代器再次从10开始,无限轮回 10→20→拦截→放行→10
Demo3:y--无符号溢出越界报错
cpp
it.repeater([=](int x, size_t& y) {
y--; // size_t无符号,index=0时-1溢出为超大数字
std::cout << "当前拦截:" << x << std::endl;
});
报错:vector::_M_range_check: __n (which is 18446744073709551615) >= this->size() (which is 3)
Demo4:forEach关闭RESET,断点续跑
cpp
// 第二个参数false:不重置索引,从上次停留位置继续遍历
it.forEach([&](int x){...},false);
五、拓展与优化方向
- 边界防护 :高危repeater修改索引时增加判断,限制index∈
[0,vec.size()-1],杜绝size_t无符号溢出崩溃; - 新增Intruder爆破接口:封装批量循环重放N次接口,对标Burp Intruder批量爆破;
- 支持反向迭代 :新增
backward()接口,原生支持游标后退; - 兼容范围for:重载begin/end,适配C++11基于范围的for循环。
六、总结
- 脱离STL原生迭代器的单向遍历限制,基于状态机+索引管控实现工业级可控遍历;
- 从安全工具Burp获取产品设计灵感,把工具功能落地成C++代码,是跨界学习编程的趣味思路;
- 两种repeater接口区分安全/高危场景,兼顾易用性和灵活性,
RESET参数完善遍历生命周期管控; - 无符号整型
size_t特性容易出现下溢大坑,实操中需要做好边界校验。
源码全部可直接编译运行,读者可自行基于本框架拓展自定义功能。
创作不易,欢迎点赞收藏,后续更新Intruder爆破接口升级版迭代器;有优化思路欢迎评论区交流~