字符集 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.,;:?! (71 个字符)。
Text 7
21052F151200271512413E35101A152F3511
固定 2-hex 替换表(步长 2)。
same script as easy level
❯ python decrypt_cipher.py 7 2密文: 21052F151200271512413E35101A152F3511******************this was confusing
Text 8
eaidagdagenpmgodlceijmgoefodlceijcnllonmgodlcfilfgamgodnnflgfgafilmgofildihdagmgoefodlccnlcnledddagmgoedddagfobdagedd
3 字符一组的替换密码。
#!/usr/bin/env python3import reimport subprocessimport sys
C = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.,;:?! "
def encrypt(text_id, text, cookie=""): cmd = ["curl", "-s"] if cookie: cmd += ["-b", cookie] cmd += [ f"https://www.trytodecrypt.com/decrypt.php?id={text_id}", "-d", f"text={text}&encrypt=Encrypt", ] r = subprocess.run(cmd, capture_output=True, text=True, timeout=10) # m = re.findall(r"panel-body[^>]>([0-9a-fA-F]+)</div>", r.stdout) m = re.findall(r"panel-body[^>]*>([0-9a-zA-Z]+)</div>", r.stdout)26 collapsed lines
return m[1] if len(m) >= 2 else None
def build_mapping(text_id, step, cookie=""): mapping = {} for i in range(0, len(C), 20): # 每次 20 字符,多数服务端限制 50 batch = C[i : i + 20] enc = encrypt(text_id, batch, cookie) if enc: for j, ch in enumerate(batch): mapping[enc[j * step : (j + 1) * step]] = ch return mapping
def decode(ct, step, mapping): return "".join(mapping.get(ct[i : i + step], "?") for i in range(0, len(ct), step))
if __name__ == "__main__": if len(sys.argv) < 3: print(f"用法: {sys.argv[0]} <text_id> <step>") sys.exit(1)
text_id = int(sys.argv[1]) step = int(sys.argv[2])
ct = sys.stdin.read().strip() if not sys.stdin.isatty() else input("密文: ").strip()
mapping = build_mapping(text_id, step) if not mapping: print("ERROR: 映射表为空,检查 text_id / step / 网络连接") sys.exit(1)
print(decode(ct, step, mapping))❯ python decrypt_cipher_alpha.py 8 3密文: eaidagdagenpmgodlceijmgoefodlceijcnllonmgodlcfilfgamgodnnflgfgafilmgofildihdagmgoefodlccnlcnledddagmgoedddagfobdagedd***************************************keep in mind: its just the middle level
Text 9
6224F12C1C3FAA5AA54836B3C446D6415E74
反转输入后,每字符映射到固定 3-hex 码。解码时反向操作。
#!/usr/bin/env python3import reimport subprocessimport sys
C = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.,;:?! "
def encrypt(text_id, text, cookie=""): cmd = ["curl", "-s"] if cookie: cmd += ["-b", cookie] cmd += [ f"https://www.trytodecrypt.com/decrypt.php?id={text_id}", "-d", f"text={text}&encrypt=Encrypt", ] r = subprocess.run(cmd, capture_output=True, text=True, timeout=10) m = re.findall(r"panel-body[^>]*>([0-9a-zA-Z]+)</div>", r.stdout) return m[1] if len(m) >= 2 else None27 collapsed lines
def build_mapping(text_id, step, cookie=""): mapping = {} # 或者把加密块大小改成 1 for i in range(0, len(C), 20): # 每次 20 字符,多数服务端限制 50 batch = C[i : i + 20] enc = encrypt(text_id, batch, cookie) if enc: for j, ch in enumerate(batch): # mapping[enc[j * step : (j + 1) * step]] = ch mapping[enc[-(j + 1) * step : len(enc) - j * step]] = ch return mapping
def decode(ct, step, mapping): return "".join(mapping.get(ct[i : i + step], "?") for i in range(0, len(ct), step))
if __name__ == "__main__": if len(sys.argv) < 3: print(f"用法: {sys.argv[0]} <text_id> <step>") sys.exit(1)
text_id = int(sys.argv[1]) step = int(sys.argv[2])
ct = sys.stdin.read().strip() if not sys.stdin.isatty() else input("密文: ").strip()
mapping = build_mapping(text_id, step) if not mapping: print("ERROR: 映射表为空,检查 text_id / step / 网络连接") sys.exit(1)
print(decode(ct, step, mapping))❯ echo "6224F12C1C3FAA5AA54836B3C446D6415E74" | python trytodecrypt_cipher_reverse.py 9 3 | rev
************fireball 123
Text 10
261129152E152B
7 -> 9 单表替换 每个字符固定映射到一个密文块,不依赖位置
10 -> 12 多表替换 映射关系随位置变化(Vigenère 风格)
步长 2,偏移量 [16, 17, 18] 循环。enc = charset_pos + offset[pos % 3]。
#!/usr/bin/env python3"""trytodecrypt CPA solver — 基于网站加密工具建映射表解码
三种模式: simple 固定映射(Text 1-8) reverse 先反转再加密(Text 9) vigenere 逐位置映射(Text 10-12) derive 自动推导公式,少量请求解码(Text 10-12 推荐)
用法: python trytodecrypt_solve.py <text_id> [options] echo '密文' | python trytodecrypt_solve.py <text_id> [options]
选项: -s, --step 步长(默认 auto) -r, --reverse 反转模式(Text 9) -v, --vigenere Vigenere 模式(逐位置建表,请求多) -d, --derive 推导模式(少量请求算公式,Text 10-12 推荐) -c, --cookie PHPSESSID -q, --quiet 静默模式180 collapsed lines
"""
import argparseimport reimport subprocessimport sysimport time
C = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.,;:?! "
def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)
def encrypt(text_id, text, cookie="", retry=3): for attempt in range(retry): cmd = ["curl", "-s", "--max-time", "15", "--retry", "2"] if cookie: cmd += ["-b", cookie] cmd += [ f"https://www.trytodecrypt.com/decrypt.php?id={text_id}", "-d", f"text={text}&encrypt=Encrypt", ] try: r = subprocess.run(cmd, capture_output=True, text=True, timeout=20) m = re.findall(r"panel-body[^>]*>([0-9a-zA-Z]+)</div>", r.stdout) if len(m) >= 2: return m[1] except subprocess.TimeoutExpired: pass if attempt < retry - 1: time.sleep(1) return None
def detect_step(text_id, cookie=""): for step in [2, 3, 4]: enc = encrypt(text_id, "0" * 10, cookie) if enc and len(enc) == step * 10: return step enc = encrypt(text_id, "0", cookie) if enc: return len(enc) return 2
def build_mapping_simple(text_id, step, cookie="", batch=20, reverse=False): mapping = {} for i in range(0, len(C), batch): plain = C[i:i + batch] enc = encrypt(text_id, plain, cookie) if not enc: continue groups = [enc[j * step:(j + 1) * step] for j in range(len(plain))] if reverse: groups.reverse() for j, ch in enumerate(plain): mapping[groups[j]] = ch return mapping
def build_mapping_vigenere(text_id, step, ct_len, cookie="", quiet=False): mapping = [{} for _ in range(ct_len)] total = ct_len * len(C) done = 0 t0 = time.time() for pos in range(ct_len): prefix = "0" * pos for ch in C: plain = prefix + ch enc = encrypt(text_id, plain, cookie) if enc: key = enc[pos * step:(pos + 1) * step] mapping[pos][key] = ch done += 1 if not quiet and done % 50 == 0: elapsed = time.time() - t0 eprint(f" [{done}/{total}] {done/total*100:.0f}% eta {elapsed/done*(total-done):.0f}s") if not quiet: eprint(f" [{total}/{total}] 100% ({time.time()-t0:.0f}s)") return mapping
def derive_vigenere(text_id, step, ct_len, cookie=""): """推导公式模式:少量请求算出 key,直接解码""" # 加密 "0"*n 获取每个位置的基值 enc_zeros = encrypt(text_id, "0" * ct_len, cookie) if not enc_zeros: return None bases = [int(enc_zeros[p * step:(p + 1) * step], 16) for p in range(ct_len)]
# 加密 "1"*n 获取梯子 enc_ones = encrypt(text_id, "1" * ct_len, cookie) if enc_ones: steps = [int(enc_ones[p * step:(p + 1) * step], 16) - bases[p] for p in range(ct_len)] else: steps = [0] * ct_len
return bases, steps
def decode_with_key(ct, step, bases, steps): """用推导出的 key 解码""" pt = "" for i in range(len(ct) // step): val = int(ct[i * step:(i + 1) * step], 16) # enc = base + cp * step if steps[i] != 0: cp = round((val - bases[i]) / steps[i]) else: cp = val - bases[i] if 0 <= cp < len(C): pt += C[cp] else: pt += "?" return pt
def decode_simple(ct, step, mapping): return "".join(mapping.get(ct[i:i + step], "?") for i in range(0, len(ct), step))
def decode_vigenere(ct, step, mapping): return "".join( mapping[i].get(ct[i * step:(i + 1) * step], "?") for i in range(len(mapping)) )
def main(): ap = argparse.ArgumentParser(description="trytodecrypt CPA solver") ap.add_argument("text_id", type=int, help="题目 ID") ap.add_argument("-s", "--step", type=int, default=0, help="步长(默认 auto)") ap.add_argument("-r", "--reverse", action="store_true", help="反转模式(Text 9)") ap.add_argument("-v", "--vigenere", action="store_true", help="Vigenere 模式(逐位置建表)") ap.add_argument("-d", "--derive", action="store_true", help="推导模式(少量请求算公式)") ap.add_argument("-c", "--cookie", default="", help="PHPSESSID") ap.add_argument("-q", "--quiet", action="store_true", help="静默模式") ap.add_argument("--batch", type=int, default=20, help="每批字符数(默认 20,simple 专用)") args = ap.parse_args()
ct = sys.stdin.read().strip() if not sys.stdin.isatty() else input("密文: ").strip() if not ct: eprint("ERROR: 未提供密文") sys.exit(1)
step = args.step or detect_step(args.text_id, args.cookie) if not args.quiet: eprint(f"[*] 步长: {step}")
if args.derive: ct_len = len(ct) // step if not args.quiet: eprint(f"[*] 推导模式: {ct_len} 个位置") result = derive_vigenere(args.text_id, step, ct_len, args.cookie) if result is None: eprint("ERROR: 推导失败") sys.exit(1) bases, steps = result if not args.quiet: eprint(f"[*] bases: {bases}") eprint(f"[*] steps: {steps}") print(decode_with_key(ct, step, bases, steps))
elif args.vigenere: ct_len = len(ct) // step if not args.quiet: eprint(f"[*] Vigenere: {ct_len} × {len(C)} = {ct_len * len(C)} 次请求") mapping = build_mapping_vigenere(args.text_id, step, ct_len, args.cookie, args.quiet) if not mapping[0]: eprint("ERROR: 映射表为空") sys.exit(1) print(decode_vigenere(ct, step, mapping))
else: mode = "reverse" if args.reverse else "simple" if not args.quiet: eprint(f"[*] 模式: {mode}") mapping = build_mapping_simple(args.text_id, step, args.cookie, args.batch, args.reverse) if not mapping: eprint("ERROR: 映射表为空") sys.exit(1) print(decode_simple(ct, step, mapping))
if __name__ == "__main__": main()❯ python trytodecrypt_solve.py -v 10 -c "8rubc5nmtqala9gvdj8t7cigqn" -s 2 -d密文: 261129152E152B[*] 步长: 2[*] 推导模式: 7 个位置[*] bases: [16, 17, 18, 16, 17, 18, 16][*] steps: [1, 1, 1, 1, 1, 1, 1]*******m0n5t3r
Text 11
3785824AD56B2531A7150DF44C21434A61E63F040A42F2012BC2F43F0AD535D24D46013213866D7E0
步长 3 hex,6 位循环。基值 [168, 282, 567, 57, 245, 180],乘数 [12, 47, 21, 19, 35, 9]。
same as Text 10
❯ python trytodecrypt_solve.py -v 11 -c "8rubc5nmtqala9gvdj8t7cigqn" -s 3 -d密文: 3785824AD56B2531A7150DF44C21434A61E63F040A42F2012BC2F43F0AD535D24D46013213866D7E0[*] 步长: 3[*] 推导模式: 27 个位置[*] bases: [168, 282, 567, 57, 245, 180, 168, 282, 567, 57, 245, 180, 168, 282, 567, 57, 245, 180, 168, 282, 567, 57, 245, 180, 168, 282, 567][*] steps: [12, 47, 21, 19, 35, 9, 12, 47, 21, 19, 35, 9, 12, 47, 21, 19, 35, 9, 12, 47, 21, 19, 35, 9, 12, 47, 21]***************************You are very good. Respect!
Text 12
00D02703603C0450461340870A50B50EA10A0BD133
基值为三角数 ,步长 。
C = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.,;:?! "ct = "00D02703603C0450461340870A50B50EA10A0BD133"vals = [int(ct[i*3:(i+1)*3], 16) for i in range(14)]print("".join(C[round((v - (i+1)*(i+2)//2) / ((i+2)/2))] for i, v in enumerate(vals)))cookie monster