一次攻击请求的内部旅程:如何读懂 smsBomb 的调度引擎
【免费下载链接】smsBomb短信💣炸🐔项目地址: https://gitcode.com/gh_mirrors/sms/smsBomb
smsBomb 是 GitHub 加速计划下的一款短信轰炸工具(Python 3,多进程 + 插件化)。本文不聊它能做什么,而是顺着一次攻击请求从命令行到发送成功的完整链路,拆解它的调度引擎:加权随机选路、失败降权、进程池并发、进度回调。读完你就能回答三个问题——并发从哪来、服务商如何被选中、失败后发生了什么。
出发前:先看一张全景图
运行python -m smsBomb -t 13800138000 -n 10之后,程序内部的旅程大致是这样的:
主线就一条:选节点 → 发请求 → 记结果 → 回调节点。下面逐站拆开看。
第一站:参数怎么变成攻击对象
入口在 smsBomb/cli.py。它先扫出smsBomb/plugins/下所有插件模块名,再读取config/sms.json(可用-p过滤出单个服务商),最后把配置、目标号码、进程数、次数打包成SmsBomb对象。
容易被忽略的是日志初始化:
def setup_logger(name, use_colors=True, verbose_count=0): level = max(5 - verbose_count, 0) * 10 logger = logging.getLogger(name) if use_colors: coloredlogs.install(logger=logger, level=level, ...) logging.getLogger('requests').setLevel(level) return logger关键在于level = max(5 - verbose_count, 0) * 10这条换算。verbose_count来自-v参数的重复次数,换算结果和大多数人以为的不一样:
| 参数 | 换算级别 | 你能看到什么 |
|---|---|---|
| 不带 -v | CRITICAL(50) | 几乎什么都不显示 |
-v | WARNING(40) | 节点失败、无此插件等警告 |
-vv | INFO(30) | 攻击开始/完毕、API 返回 |
-vvv | DEBUG(20) | 请求体、攻击进度百分比、requests 库细节 |
也就是说,-v只到 WARNING,想看攻击进度得用-vvv(进度信息是用logger.debug打的)。这一步设计动机很实际:默认静默、按需放量,避免噪音淹没关键信息。另外注意它对requests库的日志也做了统一级别设置,所以-vvv时连 HTTP 连接细节都会刷出来——这正是排查网络问题的入口。
第二站:每次攻击"选中"谁
SmsBomb.start()里是一个 while 循环,每次迭代先调_random_weight_select()挑一个配置节点:
def _random_weight_select(self): weight_lst = [[k, v.copy()] for (k, v) in enumerate(self.config_lst) for _ in range(v.get('weight', 1))] if weight_lst: return random.choice(weight_lst) return None这段代码的思路是"展开再随机":把每个配置按weight值复制多份放进列表(比如权重 3 就复制 3 份),然后random.choice一次。权重越大,被选中的概率越高。
为什么用加权随机而不是轮询?因为短信服务商质量参差不齐——某家的 app_key 可能随时被封或欠费。加权随机天然具备"概率容错":某个节点坏了,只是它被选中的概率下降,而不是整个调度卡住。观察点:v.copy()是浅拷贝,意味着后面对current_config的修改不会污染原列表(但下面你会看到,降权恰恰是写回原列表的)。
第三站:并发从哪里来
选好节点后,程序不是直接发请求,而是扔进进程池:
pool = multiprocessing.Pool(processes=self.process_num) ... success = pool.apply(worker, args=(cls, 'send', self.target), kwds=payloads)process_num默认 5(cli.py 里还做了min(process_num, times)保护,防止进程数超过攻击次数),真正干活的worker是一个薄封装:
def worker(*args, **kwargs): obj, method_name = args[:2] logging.debug('{obj}->{method_name}({args},{kwargs})'.format( obj=type(obj).__name__, method_name=method_name, args=args[2:], kwargs=kwargs)) return getattr(obj, method_name)(*args[2:], **kwargs)它只做两件事:打一条 debug 日志说明"谁调用了谁",然后getattr反射调用插件对象的send。这里有个容易忽略的点:obj是插件实例,但它是pool.apply的参数传进子进程的,所以插件类必须能被 pickle 序列化——这正是 smsBomb/smsBomb.py 里LoggerMixin用"属性动态获取 logger"而不是在__init__里self.logger = ...的原因(logger 对象无法被 pickle,注释里就引用了这个坑)。多进程下每个子进程共享一份配置快照,日志级别、格式一致,所以你看到的输出是有序的。
第四站:失败之后,节点被"降权"
如果send返回了假值(比如阿里云插件里resp['Code'] != 'OK'),就进入降权逻辑:
self.logger.warning('节点%s请求失败,尝试降低此配置的优先级...', current_config) self.config_lst[index]['weight'] = max(current_config.get('weight', 1) - 1, 0) current_config['weight'] = 1 self.failed_config_lst.append(current_config) failed_cnt += 1三步走:把原列表里该节点的weight减 1(最低到 0,0 就彻底退出选择);把副本的权重重置为 1 后塞进failed_config_lst;失败计数 +1。这套"失败降权"是调度引擎的核心策略:表现差的节点越选越少,直到全部归零。
那全部归零后呢?循环开头有句if not cfg:,此时会打 warning 并执行re_config(self.failed_config_lst)——把之前所有失败节点整体复活(它们的权重都是 1),同时失败计数 +1 再继续。这个"复活"设计很务实:没有可用节点时宁可从失败列表里重新试,也不让任务干等。观察点:re_config会清空failed_config_lst,所以复活后如果再次全灭,失败列表是从空列表重新累积的。
第五站:进度如何传出来
每次迭代末尾都会调cb(success_cnt, failed_cnt, self.limit),cb是双通道的:CLI 默认走progress_info,GUI 传入自己的回调。
def progress_info(self, success_cnt, failed_cnt, limit, force_finished=False): if success_cnt == limit or force_finished: self.logger.info('攻击完毕,成功: {0}次, 失败: {1}次'.format(...)) return self.logger.debug('攻击进度(成功数/期望攻击次数): %d/%d = %.2f%%, 实际攻击目标次数(含失败): %d(失败%d次, 失败率: %.2f%%)', ...)对应到-vvv下的真实输出,大致长这样:
[DEBUG 2018-05-31 11:29:17 smsBomb.smsBomb:168] 攻击进度(成功数/期望攻击次数): 1/10 = 10.00%, 实际攻击目标次数(含失败): 1(失败0次, 失败率: 0.00%) [INFO 2018-05-31 11:29:20 smsBomb.smsBomb:163] 攻击完毕,成功: 10次, 失败: 0次GUI 侧的回调在 smsBomb/gui.py 的refresh_progress_bar:它把成功数写进 Kivy 的NumericProperty,进度条就会自动刷新;注意force_finished时它会用attack_cnt = current_attacked_cnt把进度条停在当前值而不是强制拉满——这个小细节保证了"提前失败"时进度条是诚实的。
实战用法与避坑清单
调参看这里:
-n 50 --process 8:把攻击次数和并发提上去;权重写在config/sms.json每个节点的weight字段,想让某家服务商多干活就调大它。- 想看调度细节,用
-vvv观察三样东西:worker的调用日志、攻击进度的 debug 行、节点xxx请求失败的 warning 行。 - 0.95 的最大失败率阈值(
max_allowed_failed_rate)是硬编码在SmsBomb.__init__里的,想改只能动源码。
容易踩的坑:
-v≠ 详细日志。看到"怎么什么日志都没有",先数清楚-v的个数,-vvv才是调试态。- 进程数不是越大越好:cli.py 里
min(process_num, times)会把它限制到不大于攻击次数,-n 3 --process 10实际只开 3 个进程。 - 插件实例要跨进程传递,所以
SmsPlugin子类别在__init__里持有不可 pickle 的对象(比如直接存 logger),这是LoggerMixin用属性动态取 logger 的原因。 - 复活机制的代价:全部节点失效后会重置所有权重,可能导致"大量失败 → 全部复活 → 再来一轮",观察失败率日志判断是否该停。
最后:这套引擎能带给你什么
剥掉"短信轰炸"的外壳,smsBomb/smsBomb.py里这套"加权随机 + 失败降权 + 进程池 + 进度回调"的组合,其实是一个约 100 行的通用调度器模板。写爬虫多代理轮换、对接多供应商 API、做多路请求负载均衡时,都可以直接套用:weight控制偏好、失败降权实现自愈、cb解耦进度展示。下次你写类似系统时,不妨先照着这个骨架画一张链路图,再决定每个环节怎么实现。
【免费下载链接】smsBomb短信💣炸🐔项目地址: https://gitcode.com/gh_mirrors/sms/smsBomb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考