使用Z3破解简单的XOR加密
使用Z3破解简单的XOR加密
翻译:无名侠
原文地址: https://yurichev.com/blog/XOR_Z3/
如果我们有一段用简单XOR加密过的文本,怎么寻找密钥呢?密钥的长度可能很长,所以暴力破解不是明智的选择。
明文、密文、密钥三者的关系可以用一些简单的方程组来描述,明文可能含有尽可能多的小写字母(a....z)。
import sys, hexdump
from z3 import *
def xor_strings(s,t):
# https://en.wikipedia.org/wiki/XOR_cipher#Example_implementation
"""将两个字符串进行xor操作"""
return "".join(chr(ord(a)^ord(b)) for a,b in zip(s,t))
def read_file(fname):
file=open(fname, mode='rb')
content=file.read()
file.close()
return content
def chunks(l, n):
"""将输入的数据分成n组"""
n = max(1, n)
return [l[i:i + n] for i in range(0, len(l), n)]
def print_model(m, KEY_LEN, key):
# 从model中解析key(model()函数返回的是解
test_key="".join(chr(int(obj_to_string(m[key[i]]))) for i in range(KEY_LEN))
print "key="
hexdump.hexdump(test_key)
# 使用解出来的key解密数据:
tmp=chunks(cipher_file, KEY_LEN)
plain_attempt="".join(map(lambda x: xor_strings(x, test_key), tmp))
print "plain="
hexdump.hexdump(plain_attempt)
def try_len(KEY_LEN, cipher_file):
cipher_len=len(cipher_file)
print "len=", KEY_LEN
s=Optimize()
# key的每一个字节对应一个未知数变量:
key=[BitVec('key_%d' % i, 8) for i in range (KEY_LEN)]
# 输入密文的每一个字节对应一个未知数变量:
cipher=[BitVec('cipher_%d' % i, 8) for i in range (cipher_len)]
# 每一个明文字节对应的未知数变量:
plain=[BitVec('plain_%d' % i, 8) for i in range (cipher_len)]
# 纯文本的每个字节的变量:1如果字节在'a'…“z”范围:
az_in_plain=[Int('az_in_plain_%d' % i) for i in range (cipher_len)]
for i in range(cipher_len):
# 根据输入的密文数据给每一个密文字节变量赋值
s.add(cipher[i]==ord(cipher_file[i]))
# plain text is cipher text XOR-ed with key:
# 明文等于密文xor key
s.add(plain[i]==cipher[i]^key[i % KEY_LEN])
# 明文的每一个字节的应该是可以打印的字符,或CR 或 LF
s.add(Or(And(plain[i]>=0x20, plain[i]<=0x7E),plain[i]==0xA,plain[i]==0xD))
# 如果是小写字母则为1,否则为0.
s.add(az_in_plain[i]==If(And(plain[i]>=ord('a'),plain[i]<=ord('z')), 1, 0))
# 寻找小写字母个数最多的解。
s.maximize(Sum(*az_in_plain))
if s.check()==unsat:
return
m=s.model()
print_model(m, KEY_LEN, key)
cipher_file=read_file (sys.argv[1])
for i in range(1,20):
try_len(i, cipher_file)
#try_len(17, cipher_file)
( https://github.com/DennisYurichev/yurichev.com/blob/master/blog/XOR_Z3/1.py )
这里有一段需要被加密过的文件(350 字节):https://github.com/DennisYurichev/yurichev.com/blob/master/blog/XOR_Z3/cipher1.txt
运行尝试。
% python 1.py cipher1.txt
len= 1
len= 2
len= 3
len= 4
len= 5
...
len= 16
len= 17
key=
00000000: 90 A0 21 52 48 84 FB FF 86 83 CF 50 46 12 7A F9 ..!RH......PF.z.
00000010: 36 6
plain=
00000000: 4D 72 2E 22 54 63 65 72 6F 6F 63 6D 27 48 6F 6C Mr."Tceroocm'Hol
00000010: 6A 65 73 2C 22 70 63 6F 20 74 61 73 26 72 73 75 jes,"pco tas&rsu
00000020: 61 6B 6C 79 20 74 62 79 79 20 6F 61 74 63 27 69 akly tbyy oatc'i
00000030: 6E 20 73 68 65 20 6F 68 79 6E 69 6D 67 73 2A 27 n she ohynimgs*'
00000040: 73 61 76 62 0D 0A 75 72 68 65 20 74 6B 6F 73 63 savb..urhe tkosc
00000050: 27 6E 6F 74 27 69 6E 66 70 62 7A 75 65 6D 74 20 'not'infpbzuemt
00000060: 69 64 63 61 73 6E 6F 6E 73 22 70 63 65 6E 23 68 idcasnons"pcen#h
00000070: 65 26 70 61 73 20 72 70 20 61 6E 6B 2B 6E 69 64 e&pas rp ank+nid
00000080: 68 74 2A 27 77 61 73 27 73 65 61 76 62 6F 0D 0A ht*'was'seavbo..
00000090: 62 74 20 72 6F 65 20 62 75 65 61 6B 64 66 78 74 bt roe bueakdfxt
000000A0: 20 77 61 62 6A 62 2E 20 49 27 73 74 6F 6D 63 2B wabjb. I'stomc+
000000B0: 75 70 6C 6E 20 72 6F 65 20 68 62 61 72 74 6A 2A upln roe hbartj*
000000C0: 79 75 67 23 61 6E 62 27 70 69 63 6C 65 64 20 77 yug#anb'picled w
000000D0: 77 2B 74 68 66 0D 0A 75 73 69 63 6B 27 77 68 69 w+thf..usick'whi
000000E0: 61 6F 2B 6F 75 71 20 76 6F 74 69 74 6F 75 20 68 ao+ouq votitou h
000000F0: 61 66 27 67 65 66 77 20 62 63 6F 69 6E 64 27 68 af'gefw bcoind'h
00000100: 69 6D 22 73 63 65 20 6D 69 67 6E 73 20 62 65 61 im"sce migns bea
00000110: 6F 72 65 2C 27 42 74 20 74 61 73 26 66 0D 0A 66 ore,'Bt tas&f..f
00000120: 6E 6E 65 2C 22 73 63 69 63 68 20 70 6F 62 63 65 nne,"scich pobce
00000130: 20 68 66 20 77 6D 68 6F 2C 20 61 75 6C 64 68 75 hf wmho, auldhu
00000140: 73 2D 6F 65 61 64 67 63 27 20 6F 65 20 74 6E 62 s-oeadgc' oe tnb
00000150: 20 73 6F 75 74 20 77 6A 6E 68 68 20 6A 73 sout wjnhh js
len= 18
len= 19
这段文本并不能流畅地阅读,有趣的是,只存在17字节的key。
我们对英语文本了解多少? 数字在文本中很稀少,有一种叫做“二合字母”的东西,比如:“th”、“he”、“in”、“er” 等等,我们可以通过计算它们在文本中出现的频率,然后寻找出现次数最多的那一组解。
...
# 定义变量
# 对于每一个明文的字节变量: 1 如果这个字节是数字符号:
digits_in_plain=[Int('digits_in_plain_%d' % i) for i in range (cipher_len)]
# 对于每一个明文的字节变量: 1 如果当前字节 + 下一字节恰好等于‘th’:
th_in_plain=[Int('th_in_plain_%d' % i) for i in range (cipher_len-1)]
# ... 同理 ‘he’:
he_in_plain=[Int('he_in_plain_%d' % i) for i in range (cipher_len-1)]
in_in_plain=[Int('in_in_plain_%d' % i) for i in range (cipher_len-1)]
er_in_plain=[Int('er_in_plain_%d' % i) for i in range (cipher_len-1)]
...
for i in range(cipher_len-1):
# 约束条件
# ... 对于每一个明文的字节变量: 1 如果当前字节 + 下一字节恰好等于‘th’:
s.add(th_in_plain[i]==(If(And(plain[i]==ord('t'),plain[i+1]==ord('h')), 1, 0)))
# ... etc:
s.add(he_in_plain[i]==(If(And(plain[i]==ord('h'),plain[i+1]==ord('e')), 1, 0)))
s.add(in_in_plain[i]==(If(And(plain[i]==ord('i'),plain[i+1]==ord('n')), 1, 0)))
s.add(er_in_plain[i]==(If(And(plain[i]==ord('e'),plain[i+1]==ord('r')), 1, 0)))
# 寻找小写字母个数最多的解。
s.maximize(Sum(*az_in_plain))
# ... 且数字出现次数最少的解
s.minimize(Sum(*digits_in_plain))
# "th", "he", "in" , "er" 出现次数最多的解
s.maximize(Sum(*th_in_plain))
s.maximize(Sum(*he_in_plain))
s.maximize(Sum(*in_in_plain))
s.maximize(Sum(*er_in_plain))
...
( https://github.com/DennisYurichev/yurichev.com/blob/master/blog/XOR_Z3/2.py )
现在我们再从次执行脚本,好像更接近原文了
len= 17
key=
00000000: 90 A0 22 50 4F 8F FB FF 85 83 CF 56 41 12 7A FE .."PO......VA.z.
00000010: 31 1
plain=
00000000: 4D 72 2D 20 53 68 65 72 6C 6F 63 6B 20 48 6F 6B Mr- Sherlock Hok
00000010: 6D 65 73 2F 20 77 68 6F 20 77 61 73 20 75 73 75 mes/ who was usu
00000020: 66 6C 6C 79 23 76 65 72 79 20 6C 61 74 65 20 69 flly#very late i
00000030: 6E 27 74 68 65 23 6D 6F 72 6E 69 6E 67 73 2C 20 n'the#mornings,
00000040: 73 61 71 65 0D 0A 76 70 6F 6E 20 74 68 6F 73 65 saqe..vpon those
00000050: 20 6E 6F 73 20 69 6E 65 72 65 71 75 65 6E 74 20 nos inerequent
00000060: 6F 63 63 61 74 69 6F 6E 70 20 77 68 65 6E 20 68 occationp when h
00000070: 65 20 77 61 73 27 75 70 20 62 6C 6C 20 6E 69 67 e was'up bll nig
00000080: 68 74 2C 20 77 61 74 20 73 65 62 74 65 64 0D 0A ht, wat sebted..
00000090: 61 74 20 74 68 65 20 65 72 65 61 68 66 61 73 74 at the ereahfast
000000A0: 20 74 61 62 6C 65 2E 20 4E 20 73 74 6C 6F 64 20 table. N stlod
000000B0: 75 70 6F 6E 20 74 68 65 20 6F 65 61 72 77 68 2D upon the oearwh-
000000C0: 72 75 67 20 61 6E 64 20 70 69 64 6B 65 64 23 75 rug and pidked#u
000000D0: 70 20 74 68 65 0D 0A 73 74 69 63 6C 20 77 68 6A p the..sticl whj
000000E0: 63 68 20 6F 75 72 20 76 69 73 69 74 68 72 20 68 ch our visithr h
000000F0: 62 64 20 6C 65 66 74 20 62 65 68 69 6E 63 20 68 bd left behinc h
00000100: 69 6E 20 74 68 65 20 6E 69 67 68 74 20 62 62 66 in the night bbf
00000110: 6F 72 66 2E 20 49 74 20 77 61 73 20 61 0D 0A 61 orf. It was a..a
00000120: 69 6E 65 2F 20 74 68 69 63 6B 20 70 69 65 63 65 ine/ thick piece
00000130: 27 6F 66 20 74 6F 6F 64 2C 20 62 75 6C 62 6F 75 'of tood, bulbou
00000140: 73 2A 68 65 61 67 65 64 2C 20 6F 66 20 74 68 65 s*heaged, of the
有几个字符还是有错误的,但是我们可以手动修复它们,因此添加限制条件。(不直接改原文的原因是我们的目标是寻找key,所以通过增加确定性限制条件重新求解key)
...
# 3 known characters of plain text:
s.add(plain[0xf]==ord('l'))
s.add(plain[0x20]==ord('a'))
s.add(plain[0x57]==ord('f'))
...
( https://github.com/DennisYurichev/yurichev.com/blob/master/blog/XOR_Z3/3.py )
这一次key正确了:
len= 17
key=
00000000: 90 A0 21 50 4F 8F FB FF 85 83 CF 56 41 12 7A F9 ..!PO......VA.z.
00000010: 31 1
plain=
00000000: 4D 72 2E 20 53 68 65 72 6C 6F 63 6B 20 48 6F 6C Mr. Sherlock Hol
00000010: 6D 65 73 2C 20 77 68 6F 20 77 61 73 20 75 73 75 mes, who was usu
00000020: 61 6C 6C 79 20 76 65 72 79 20 6C 61 74 65 20 69 ally very late i
00000030: 6E 20 74 68 65 20 6D 6F 72 6E 69 6E 67 73 2C 20 n the mornings,
00000040: 73 61 76 65 0D 0A 75 70 6F 6E 20 74 68 6F 73 65 save..upon those
00000050: 20 6E 6F 74 20 69 6E 66 72 65 71 75 65 6E 74 20 not infrequent
00000060: 6F 63 63 61 73 69 6F 6E 73 20 77 68 65 6E 20 68 occasions when h
00000070: 65 20 77 61 73 20 75 70 20 61 6C 6C 20 6E 69 67 e was up all nig
00000080: 68 74 2C 20 77 61 73 20 73 65 61 74 65 64 0D 0A ht, was seated..
00000090: 61 74 20 74 68 65 20 62 72 65 61 6B 66 61 73 74 at the breakfast
000000A0: 20 74 61 62 6C 65 2E 20 49 20 73 74 6F 6F 64 20 table. I stood
000000B0: 75 70 6F 6E 20 74 68 65 20 68 65 61 72 74 68 2D upon the hearth-
000000C0: 72 75 67 20 61 6E 64 20 70 69 63 6B 65 64 20 75 rug and picked u
000000D0: 70 20 74 68 65 0D 0A 73 74 69 63 6B 20 77 68 69 p the..stick whi
000000E0: 63 68 20 6F 75 72 20 76 69 73 69 74 6F 72 20 68 ch our visitor h
000000F0: 61 64 20 6C 65 66 74 20 62 65 68 69 6E 64 20 68 ad left behind h
00000100: 69 6D 20 74 68 65 20 6E 69 67 68 74 20 62 65 66 im the night bef
00000110: 6F 72 65 2E 20 49 74 20 77 61 73 20 61 0D 0A 66 ore. It was a..f
00000120: 69 6E 65 2C 20 74 68 69 63 6B 20 70 69 65 63 65 ine, thick piece
00000130: 20 6F 66 20 77 6F 6F 64 2C 20 62 75 6C 62 6F 75 of wood, bulbou
00000140: 73 2D 68 65 61 64 65 64 2C 20 6F 66 20 74 68 65 s-headed, of the
00000150: 20 73 6F 72 74 20 77 68 69 63 68 20 69 73 sort which is
因此,这就是正确的key。
不用说,输入的数据越大越好。这个350字节的文件实际上是另一个更大加密文件的前戏(cipher2.txt, 12903 bytes) ,这个大文件的key可以不用添加那些些启发式条件也能求得。
SMT Solver 可以秒杀这些东西,我曾经很天真的解决了这些问题,这里有一个更快的版本: https://yurichev.com/blog/XOR_mask_2/尽管如此,这仍然是另一个优化问题的演示。
使用Z3破解简单的XOR加密的更多相关文章
- XOR 加密简介
本文介绍一种简单高效.非常安全的加密方法:XOR 加密. 一. XOR 运算 逻辑运算之中,除了 AND 和 OR,还有一种 XOR 运算,中文称为"异或运算". 它的定义是:两个 ...
- 手游Apk破解疯狂,爱加密apk加固保护开发人员
2013年手游行业的规模与收入均实现了大幅增长,发展势头强劲.权威数据显示, 我国移动游戏市场实际销售收入从2012年的32.4亿猛增到2013年的112.4亿元,同比增长了246.9%,手游用户从2 ...
- XOR 加密
XOR 是一个神奇的运算符, 观察它的真值表, 很容易得到以下结论: 假设现有 a , b 变量, 则 a ^ 0 == a a ^ 0xff == ~a (取反加1等于作为补码的a的真值的相反数的补 ...
- 完全教程 Aircrack-ng破解WEP、WPA-PSK加密利器
其 实关于无线基础知识的内容还是挺多的,但是由于本书侧重于BT4自身工具使用的讲解,若是再仔细讲述这些外围的知识,这就好比讲述DNS工具时还要把 DNS服务器的类型.工作原理及配置讲述一遍一样,哈哈, ...
- 用imagemagick和tesseract-ocr破解简单验证码
用imagemagick和tesseract-ocr破解简单验证码 Tesseract-ocr据说辨识程度是世界排名第三,可谓神器啊. 准备工作: 1.安装tesseract-ocr sudo apt ...
- C#,ASP.NET简单的MD5加密,解密
简单的MD5加密 首先要有一个加解密的规则 就是key 代码如下 // 创建Key public string GenerateKey() { DESCryptoServiceProvider de ...
- python+selenium十三:破解简单的图形验证码
此方法可破解简单的验证码,如: 注:中文识别正在寻找办法 安装: 1.python3 2.Pillow 3.pytesseract 4.tesseract-ocr 下载地址:https://pa ...
- javascript 异或运算符实现简单的密码加密功能
写在前面的 当我们需要在数据库中存储用户的密码时,当然是不能明文存储的. 我们就是介绍一下用^运算符来实现简单的密码加密以及解密功能 上代码 首先,回顾一下基础知识. String.fromCharc ...
- 【Python3爬虫】反反爬之破解同程旅游加密参数 antitoken
一.前言简介 在现在各个网站使用的反爬措施中,使用 JavaScript 加密算是很常用的了,通常会使用 JavaScript 加密某个参数,例如 token 或者 sign.在这次的例子中,就采取了 ...
随机推荐
- bfs学习
今天做到了bfs的练习,顺便写下心得... bfs能解决搜索和最短路径的问题. 下面是学习心得: typedef struct point //定义点 { int x; int y; }P; bfs( ...
- 获取屏幕宽高度与可视区域宽高度(availWidth、clientWidth、width、innerWidth)
经常会遇到需要获取屏幕宽度.高度,可视区域宽度.高度等问题,也就常跟这几个打交道,一不小心,还真爱弄混淆了. 先来列举下这几个吧: screen.availHeight.screen.availWid ...
- js获取不带单位的像素值
所谓获取不带单位的像素值就是获取比如元素的宽度.高度.字体大小.外边距.内边距等值但是去掉像素单位. 比如:某一个元素的宽度是100px,现在我要获取这个这个值但是不带单位“px”,对于这种问题你会怎 ...
- [UWP]使用Acrylic
1. 前言 在 如何使用Fluent Design System 这篇文章里已经简单介绍过Reveal的用法,这篇再详细介绍其它内容. 自Windows 8 放弃Aero后,群众对毛玻璃回归的呼声一致 ...
- Java-String.intern的深入研究
When---什么时候需要了解String的intern方法: 面试的时候(蜜汁尴尬)!虽然不想承认,不过面试的时候经常碰到这种高逼格的问题来考察我们是否真正理解了String的不可变性.String ...
- php-cli和php-fpm的关系是什么?
CLI是命令行版本.FPM是作为Apache或者Nginx等服务器软件处理PHP文件的扩展. PHP的默认版本是不支持线程安全的,这个线程安全问题可以去了解PHP扩展开发方面的相关知识,这是因为C语言 ...
- ecshop商城_
一.Ecshop简介: ECShop是Comsenz公司推出的一款B2C独立网店系统,适合企业及个人快速构建个性化网上商店.系统是基于PHP语言及MYSQL数据库构架开发的跨平台开源程序. ECSho ...
- 版本控制——TortoiseSVN (3)多版本发布
=================================版权声明================================= 版权声明:原创文章 禁止转载 请通过右侧公告中的“联系邮 ...
- 【视频编解码·学习笔记】2. H.264简介
一.H.264视频编码标准 H.264视频编码标准是ITU-T与MPEG合作产生的又一巨大成果,自颁布之日起就在业界产生了巨大影响.严格地讲,H.264标准是属于MPEG-4家族的一部分,即MPEG- ...
- 引用类型之数组array最全的详解
Array类型 今天总结一下array类型. js中的数组是有着非常强大的功能.具有很大的灵活性,有两个方面的特点 1.数组的每一项可以保存任何的数据类型:2.数组大小可以动态的调整:看下面的例子: ...