挖edusrc,很多自动化工具扫出的ip,要揉在语句里放到kali进行验证。一个一个复制加修改很麻烦,于是萌生了写一个辅助脚本的想法,也以此学一下python使用,使学习贴于实际
这也是开始学python后写的第一段脚本
我的学习过程
先让ai写了完整脚本,然后丢到另一个ai中,让他给我逐句解释,让我对每个函数每个功能进行复现。最后开始整体的脚本复现,搜改,升级,搜改。
从中学到了很多,也不仅仅是py语法方面的,还有对安全工具开发等多维度进行了学习
脚本介绍:
从一个文档中提取纯净的ip,揉进特定语句中输出到控制台,然后输出那些被我标记通过的ip到另一个文件里
起到整理作用
源码:
1 | import os |
用到的功能点:
| 函数 | 功能 | 用到的技术点 |
|---|---|---|
resolve_path |
路径解析 | os.path.dirname、os.path.abspath、os.path.join |
ipfind |
读文件、清洗、去重 | with open、strip、re.search、match.group(0)、dict.fromkeys(去重有序,相较于集合) |
output_cons |
循环展示、用户确认 | input().strip().lower()、for、+= |
save_result |
追加写入 | open(..., "a")、f.write(ip + "\n") |
main |
串联、异常处理 | try-except KeyboardInterrupt、is None |
f.write(ip + “\n”) vs f.write(“\n”.join(…)):注意循环中用前者,后者的话会输出整个列表,会把之前输出的再次输出
思维与心态收获:
| 蓝图设计 | 写代码前先想清楚目标、输入、输出、模块、数据流 |
|---|---|
| 求败战术 | 主动找错,越错越好,错误是修正认知的唯一途径 |
| 效率复盘 | 改代码前先列清单,每写一个函数就跑一次 |
| 工具适用 | 需要考虑到工具适用性,如脏数据处理,返回值清晰。 |
蓝图设计:
ai所教
模板:
【脚本名称】 【一句话目标】 【输入】 - 输入1:格式、来源 - 输入2:格式、来源 【输出】 - 输出1:格式、去向 - 输出2:格式、去向 【功能模块】 1. 模块A:负责什么,输入什么,输出什么 2. 模块B:负责什么,输入什么,输出什么 3. ... 【数据流】 main → 模块A → 模块B → 输出 【异常处理】 - 文件不存在怎么办? - 用户输入非法怎么办? - 网络超时怎么办? 【待办】 - 第一版先实现什么 - 第二版再加什么
例子:
【脚本名称】Redis 未授权验证辅助脚本
【一句话目标】
帮安全测试人员,解决「手动输入 IP 执行 Redis 未授权验证命令时容易输错、效率低」的问题。【输入】
- a_ip.txt:txt,每行一个 IP(可能带 http:// 或 :6379 等多余字符)
- 用户确认:键盘输入 y 或回车
【输出】
- a_ips.txt:txt,每行一个确认成功的 IP
【功能模块】
ipload()
输入:无(用户交互输入文件名)
输出:IP 列表(去重保序),或 None(文件不存在)
职责:读文件、正则提取 IP、跳过空行、去重保序output_cons(ips)
输入:IP 列表
输出:确认数量(int)
职责:清空 a_ips.txt → 循环每个 IP → 打印命令 → 问用户 → 如果 y 则立即追加写入 a_ips.txtmain()
输入:无
输出:无
职责:串联 ipload 和 output_cons,处理异常【数据流】
main → ipload → 返回 ips
→ 如果 ips 为 None 或空 → 退出
→ output_cons(ips) → 边确认边写 a_ips.txt
→ 打印总结【异常处理】(第二版加)
- 文件不存在:ipload 返回 None
- 用户 Ctrl+C:main 加 try-except KeyboardInterrupt
- 写文件失败:output_cons 加 try-except
【待办】
- 第一版:跑通基本流程
- 第二版:加异常处理
- 第三版:加分批(每 5 个停一下)
提高效率:
不要【写->改->写】,而是要先【抄】
先理解源代码,然后照着源代码打一遍,接着闭卷打一遍,搜,写出修改清单,改,最后复现完整,写总结
检查清单(ai生成)
以后的问题可以先自查一下,都是我在与ai交流过程中遇到的问题
一、路径与文件类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| P1 | 相对路径找不到文件 | 相对路径相对于当前工作目录,不是脚本所在目录 | 用 resolve_path():os.path.dirname(os.path.abspath(__file__)) + os.path.join |
工具里所有文件路径都过 resolve_path() |
| P2 | 文件不存在时返回 [] |
[] 既表示「文件不存在」,也表示「文件为空」 |
文件不存在返回 None,文件为空返回 [] |
用 is None 区分「不存在」和「空」 |
| P3 | 空行被加入列表 | line.strip() 后空行变成 "",正则匹配不到,走 else 被 append |
加 if not line: continue |
读文件时先跳过空行 |
| P4 | f.write("\n".join(...)) 在循环里重复写 |
join 拼出整个列表,每次确认都把之前所有内容再写一遍 |
循环中用 f.write(ip + "\n") |
循环里只写当前元素,一次性写才用 join |
| P5 | 结果文件只在程序结束后更新 | output_file 在 output_cons 全部循环完之后才调用 |
用 "a" 模式边确认边写 |
自动工具边扫边写,防止中途 Ctrl+C 丢数据 |
| P6 | "w" 和 "a" 模式混淆 |
"w" 覆盖清空,"a" 追加 |
循环前用 "w" 清空一次,循环中用 "a" 追加 |
记住:w=覆盖,a=追加 |
| P7 | 用户输入空文件名导致 open("") 报错 |
用户直接回车,output_filename 是 "" |
加 if not output_filename: output_filename = "result.txt" |
所有用户输入的文件名都要处理默认值 |
| P8 | 从别的目录运行脚本找不到文件 | 相对路径依赖当前工作目录 | 用 resolve_path() 转成相对于脚本目录的绝对路径 |
工具不能依赖「你在哪个目录运行它」 |
二、字符串与正则类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| S1 | strip() 没去掉中间空格 |
strip() 只去首尾空白,不去中间 |
用 replace(" ", "") 或 "".join(line.split()) |
先确认需求:去首尾还是去所有 |
| S2 | match.group(0) 不理解 |
不知道 match 是 Match 对象,不是字符串 |
group(0) 是整个匹配,group(1) 是第1个括号 |
正则里括号是分组,没括号只有 group(0) |
| S3 | re.search 匹配失败后没处理 |
匹配不到时 match 是 None,直接 match.group(0) 会报 AttributeError |
先 if match: 判断,再取 group(0) |
所有 re.search 后都要判断 if match |
| S4 | 空行被 re.search 匹配后加入列表 |
空行 strip() 后是 "",正则匹配不到,走 else 被 append |
加 if not line: continue |
读文件时先跳过空行 |
| S5 | input() 拿到脏数据 |
用户可能输大写、带空格、带换行 | .strip().lower() |
所有 input() 都清洗 |
三、数据结构类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| D1 | set() 无序,每次运行顺序不一样 |
集合内部用哈希表,存储顺序和插入顺序无关 | 用 list(dict.fromkeys(...)) 去重保序 |
需要顺序时不用 set |
| D2 | dict.fromkeys(match) 用错 |
match 是 Match 对象,不是列表 |
先收集到列表,循环结束后 dict.fromkeys(cleaned_ips) |
dict.fromkeys 的参数必须是可迭代对象 |
| D3 | return list(dict.fromkeys(match)) 放在循环里 |
循环只执行一次就返回 | return 放在循环外面 |
写函数前先想清楚返回值在循环内还是循环外 |
| D4 | cleaned_ips 定义在 if 里面 |
如果 if 不成立,变量未定义 |
变量定义在函数开头 | 变量定义放在函数最前面 |
四、控制流与函数类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| C1 | return 写在循环里 |
循环只执行一次就返回 | return 放循环外 |
写函数前先想清楚返回值 |
| C2 | else 对不上 if |
if 和 else 中间插了别的语句 |
if...else 必须紧挨着 |
注意缩进和结构 |
| C3 | else: continue 多余 |
if 不匹配时自动进入下一轮循环 |
删掉 else: continue |
continue 只在需要跳过时用 |
| C4 | count_all 和 count_confirm 没定义 |
直接 += 1 会报 NameError |
循环前先 count_all = 0 |
Python 变量必须先赋值再使用 |
| C5 | main 里 count_all 可能未定义 |
如果 cleaned_ips 为空,count_all 没赋值就打印 |
加 return 提前退出 |
分支里提前 return,避免未定义变量 |
| C6 | if not cleaned_ips: 后面缺 return |
空列表时继续往下执行 | 加 return |
每个分支都要想清楚「然后呢」 |
| C7 | 函数参数名和循环变量名冲突 | for i in ip: 但里面用 ip |
列表用复数 ips,单个用单数 ip |
命名规范:列表复数,元素单数 |
| C8 | output_cons 说「无传出」但 output_file 需要数据 |
数据流断裂 | output_cons 返回确认列表,或边确认边写文件 |
写蓝图时画出数据流 |
五、Python 语法类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| G1 | count++ |
Python 没有 ++ |
count += 1 |
记住 Python 语法 |
| G2 | return a and b |
and 返回单个值(True/False或最后一个值) |
return a, b 返回元组 |
多返回值用逗号 |
| G3 | a and b = 函数() |
左边不能是表达式 | a, b = 函数() |
元组解包左边只能是变量 |
| G4 | == None |
不规范 | is None |
None 是单例,用 is |
| G5 | except KeyboardInterrupt : |
冒号前有空格 | except KeyboardInterrupt: |
注意语法规范 |
| G6 | if not filename: 后没处理默认值 |
用户直接回车,filename 是 "" |
if not filename: filename = "ip.txt" |
默认值处理放在函数开头 |
六、效率与流程类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| E1 | 改代码只改记得住的 | 没列「本次要改的问题」清单 | 改代码前先列清单,改一个划一个 | 清单是外部记忆,不依赖脑子 |
| E2 | 一口气写完整脚本再跑 | 错误互相掩盖 | 每写一个函数就跑一次 | 错误隔离在单个函数里 |
| E3 | 卡住超过10分钟还不查 | 自己死磕 | 卡住5分钟就查文档/搜/问 | 借鉴前人,不死磕 |
| E4 | 一遍过,不做三连刷题 | 只做了「记忆」,没做「理解」和「运用」 | 第一遍抄,第二遍合上写,第三遍改功能 | 三连刷题法 |
| E5 | 跳过最小可运行版本 | 直接写完整脚本 | 先跑最小版本,逐步加功能 | 每步只加一个功能,跑通再加下一个 |
| E6 | 写→改→写,效率低 | 没有先抄 | 先理解源代码,照着打一遍,闭卷打一遍,搜,写修改清单,改,复现完整,写总结 | 先抄再改 |
七、工具设计类
| 编号 | 坑 | 原因 | 修正 | 下次怎么避免 |
|---|---|---|---|---|
| T1 | 输入输出文件名前后不一致 | 设计时说 a_ip.txt,写代码时用 ip.txt |
统一命名 | 蓝图里写清楚,写代码时对照 |
| T2 | output_cons 说「无传出」但 output_file 需要数据 |
数据流断裂 | 要么返回列表,要么边确认边写文件 | 写蓝图时画出数据流 |
| T3 | 没有异常处理 | 文件不存在、用户Ctrl+C、写文件失败 | 加 try-except |
工具越自动,越要处理异常 |
| T4 | 没有区分「文件不存在」和「文件为空」 | 都返回 [] |
文件不存在返回 None,文件为空返回 [] |
用 is None 区分 |
| T5 | 工具依赖当前工作目录 | 相对路径 | 用 resolve_path() |
工具不能依赖「你在哪个目录运行它」 |
