使用零宽字符对文本加密的实现
> 本文为 www.52pojie.cn 首发> 《使用零宽字符对文本加密的实现》
> @TLHorse
# 0x0 前言
说来话长。其实就是前几天我看到了一篇介绍Unicode的文章,里面介绍Unicode字符的广泛性。其中有一类字符叫做零宽字符,它们在电脑上输入,不可见,也不可打印,甚至输入都不会占空间,作用是控制文字排列或解决个别语言中的排版问题。
常见的零宽字符有以下六种:
| 中文名 | 英文名 | U+ | 作用 |
| -------- | -------- | -------- | --------- |
| 零宽度空格符 | zero-width space | U+200B | 用于较长单词的换行分隔。 |
| 零宽度非断空格符 | zero width no-break space | U+FEFF | 用于阻止特定位置的换行分隔。|
| 零宽度连字符 | zero-width joiner | U+200D | 用于阿拉伯文与印度语系等文字中,使不会发生连字的字符间产生连字效果。|
| 零宽度断字符 | zero-width non-joiner | U+200C | 用于阿拉伯文、德文、印度语系等文字中,阻止会发生连字的字符间的连字效果。|
| 左至右符 | left-to-right mark |U+200E|用于在混合文字方向的多种语言文本中(例:混合左至右书写的英语与右至左书写的希伯来语),规定排版文字书写方向为左至右。|
| 右至左符 |right-to-left mark|U+200F|用于在混合文字方向的多种语言文本中,规定排版文字书写方向为右至左。|
使用这些零宽字符可以实现发空白消息、发空白朋友圈等效果。不妨动脑筋一想,我们可以把这几种字符组合起来,就可以实现加密字符串的效果。
# 0x1 基本思路
这篇文章咱们只介绍一个初步实现,说不定以后我会出后续,给应用添加更多功能。下面是加解密的流程图。
总之我认为解密过程较为繁琐。
# 0x2 核心实现
激动人心的时刻到啦。让我们一步步编写。
首先在全局定义三个常量:
```python
ZW_ONE = u"\u200b" # 用来翻译1
ZW_ZERO = u"\u200c" # 用来翻译0
ZW_SEP = u"\u200d" # 用来翻译字符之间的间隔
```
## 加密函数 `str2zwstr(origin)`
首先我们新建一个空数组,用来存储字符串每一项,并且遍历明文,使明文的每个字符**先用`ord()`转换成十进制数字,再用`bin()`转换成二进制。再转换成字符串格式,最后去掉`0b`前缀**:
```python
bin_text = []
for char in origin:
bin_text.append(str(bin(ord(char))).lstrip('0b'))
```
再创建一个空字符串,用来存储最终的结果:
```python
final_str = ""
```
之后进行两次遍历,先浅层遍历`bin_text`,然后为每一项深层遍历:
```python
for item in bin_text: # 遍历大数组每一项
for binchar in item: # 遍历每一项中的0和1
final_str += ZW_ONE if binchar == "1" else ZW_ZERO # 把0、1分别翻译成两种零宽度字符串
final_str += ZW_SEP # 每一项(字符)结束后,插入一个分隔符号
final_str.rstrip(ZW_SEP) # 去掉
return final_str
```
最后返回`final_str`即可。
整体代码:
```
def str2zwstr(origin):
bin_text = []
for char in origin:
bin_text.append(str(bin(ord(char))).lstrip('0b'))
final_str = ""
for item in bin_text:
for binchar in item:
final_str += ZW_ONE if binchar == "1" else ZW_ZERO
final_str += ZW_SEP
final_str.rstrip(ZW_SEP)
return final_stry
```
## 解密函数`zwstr2str(enc_str)`
首先我建议大家再看看基本思路中的流程图。解密不大相同。因为一开始我们要把翻译后的数据存储到第一个字符上,但是遇到分隔符后,我们又得新建一个字符,并把接下来的翻译后的数据存储到第二个字符串上,因此我们要编写一个函数`apponlast(arr, sth)`,每次运行,都将sth拼接到arr的最后一项中,如果arr的项数为0,即新增一个元素。
我这里用的代码极为简洁:
```python
def apponlast(arr, sth):
la = len(arr) # la 是 arr 的长度
if la: arr += sth # 如果长度不为0,那么就把 arr 的最后一项与 sth 字符串拼接
else: arr.append(sth) # 如果数组里没有元素,新建一个元素
```
之后编写解密函数:
```python
def zwstr2str(enc_str):
arr_oz = [] # 由0和1构成的字符串构成的数组
for char in enc_str: # 在密文里遍历
if char == ZW_ONE: apponlast(arr_oz, "1") # 如果是\200b,翻译成1
elif char == ZW_ZERO: apponlast(arr_oz, "0") # 如果是\200c,翻译成0
elif char == ZW_SEP: arr_oz.append("") # 如果是分隔符,把数组新建一项,重新开始循环
else: print("Input contains non-ZW string. Aborted."); getinput() # 如果密文中有非零宽字符,终止解密并回到程序主函数(我们一会要编写)
for idx in range(0, len(arr_oz)-1): # 遍历这个由0和1构成的字符串构成的数组
arr_oz = chr(int(arr_oz, 2)) # 把每一项先转换为int(注意二进制参数),然后用chr转换为字符
return "".join(arr_oz) # 拼接
```
# 完善程序
接下来我们为程序添加一个主函数,并且加些花哨的功能。
```python
if __name__ == '__main__':
pbanner()
getinput()
```
`pbanner()`用来打印banner:
```python
def pbanner():
banner = colored(f"""
______
/___/\\Zerowidth String Encoder | @TLHorse from 52pojie
/// / \\\\ Type in then ENTER. The encoded string
\\\\ / /__// will be copied & printed.
\/_____/Commands | ::openweb:: ::banner:: ::quit:: ::switchmode::
""", 'yellow')
print(banner)
```
需要说明一下,上面的`colored`函数需要依赖一个第三方库`termcolor`,可以打印出彩色字符串。
我们在全局设置两个变量:
```python
MODE_ENCODE = True # 用来记录模式是加密还是解密
LAST_RESULT = "" # 用来记录上次操作的结果
```
`getinput`是一个递归,可以像命令行一样获取用户输入,代码比较复杂,功能很多,本来是有注释的,结果浏览器编辑的时候不小心给关了,没有恢复成功。大家自己摸索吧:
```python
def getinput():
global MODE_ENCODE, LAST_RESULT
info = ""
if MODE_ENCODE: info = colored("ENCODE", 'red')
else: info = colored("DECODE", 'green')
input_str = input(f'[{info}] ')
if input_str == '::openweb::': os.system('open https://www.52pojie.cn') # 打开吾爱网页
elif input_str == '::banner::': pbanner() #打印banner
elif input_str == '::quit::': sys.exit(0) #退出程序
elif input_str == '::switchmode::': MODE_ENCODE = False if MODE_ENCODE else True #切换加解密
elif input_str == '::cp::': os.system(f'echo {LAST_RESULT} | pbcopy') #复制结果
if input_str.startswith("::") and input_str.endswith("::"): getinput() #检测是否为命令
out = str2zwstr(input_str) if MODE_ENCODE == True else zwstr2str(input_str)
print(colored(' >>> "', 'green')+ out + colored('"', 'green'))
LAST_RESULT = out
getinput()
```
别忘了import进类库:
```python
import os, sys
from termcolor import colored
```
大功告成!
# 后记
首先,想说明一点,文中的结果复制功能是基于pbcopy命令的,这个只有Linux和Unix有,Windows没有。所以Windows小伙伴们记得使用`pyperclip`库实现复制功能。
其次,我也不是程序员,所以代码的繁琐与不妥当之处欢迎跟帖指正。
最后,这篇文章很有可能会出续集哦!
所有代码长这样:
```python
import os, sys
from termcolor import colored
ZW_ONE = u"\u200b"
ZW_ZERO = u"\u200c"
ZW_SEP = u"\u200d"
MODE_ENCODE = True
LAST_RESULT = ""
def pbanner():
banner = colored(f"""
______
/___/\\Zerowidth String Encoder | @TLHorse from 52pojie
/// / \\\\ Type in then ENTER. The encoded string will be printed.
\\\\ / /__// Commands | ::openweb:: ::banner:: ::quit:: ::switchmode::
\/_____/::cp::
""", 'yellow')
print(banner)
def apponlast(arr, sth):
la = len(arr)
if la: arr += sth
else: arr.append(sth)
def str2zwstr(origin):
bin_text = []
for char in origin:
bin_text.append(str(bin(ord(char))).lstrip('0b'))
final_str = ""
for item in bin_text:
for binchar in item:
final_str += ZW_ONE if binchar == "1" else ZW_ZERO
final_str += ZW_SEP
final_str.rstrip(ZW_SEP)
return final_str
def zwstr2str(enc_str):
arr_oz = []
for char in enc_str:
if char == ZW_ONE: apponlast(arr_oz, "1")
elif char == ZW_ZERO: apponlast(arr_oz, "0")
elif char == ZW_SEP: arr_oz.append("")
else: print("Input contains non-ZW string. Aborted."); getinput()
for idx in range(0, len(arr_oz)-1):
arr_oz = chr(int(arr_oz, 2))
return "".join(arr_oz)
def getinput():
global MODE_ENCODE, LAST_RESULT
info = ""
if MODE_ENCODE: info = colored("ENCODE", 'red')
else: info = colored("DECODE", 'green')
input_str = input(f'[{info}] ')
if input_str == '::openweb::': os.system('open https://www.52pojie.cn')
elif input_str == '::banner::': pbanner()
elif input_str == '::quit::': sys.exit(0)
elif input_str == '::switchmode::': MODE_ENCODE = False if MODE_ENCODE else True
elif input_str == '::cp::': os.system(f'echo {LAST_RESULT} | pbcopy')
if input_str.startswith("::") and input_str.endswith("::"): getinput()
out = str2zwstr(input_str) if MODE_ENCODE == True else zwstr2str(input_str)
print(colored(' >>> "', 'green')+ out + colored('"', 'green'))
LAST_RESULT = out
getinput()
if __name__ == '__main__':
pbanner()
getinput()
```
代码下载在这里:链接:https://share.weiyun.com/PyUOPC6F 密码:zsetlh
有趣的代码,哈哈哈 感觉可以完成那种普通编辑器打开是一篇文章,这个程序打开是信息的那种加密
有点像谍战片里面的一本书是密码本那种{:301_1009:} 思路清奇! 这样加密的话体积是不是变成原来的64倍了 \u200b就在vx上碰到过,下载下来保存不了,仔细看就有它{:1_908:}
页:
[1]