有人熟悉 Windows 世界中的这种混淆方案吗?
一旦你正确理解它,它只是一个像ROT13这样的微不足道的旋转密码。
为什么会有人用这个?
嗯,一般来说,这很常见。假设您有一些需要混淆的数据。但是解密算法和密钥必须嵌入到观众拥有的软件中。使用像 AES 这样的花哨的东西是没有意义的,因为有人总是可以从你的代码中挖掘算法和密钥,而不是破解 AES。比找到隐藏密钥更难破解的加密方案与完美的加密方案一样好——也就是说,足以阻止普通观众,而对严重的攻击者毫无用处。(通常你甚至并不真正担心停止攻击,但要在事实之后证明您的攻击者出于合同/法律原因必须恶意行事。)因此,您使用简单的旋转密码或简单的异或密码 - 它很快,很难出错并且容易进行调试,如果最坏的情况发生,您甚至可以手动解密以恢复损坏的数据。
至于细节:
如果要处理非 ASCII 字符,则几乎必须使用 Unicode。如果您使用一些固定的 8 位字符集或本地系统的 OEM 字符集,您将无法处理来自其他机器的密码。
Python 脚本几乎肯定会处理 Unicode 字符,因为在 Python 中,您要么处理 a 中的字节,要么处理 a 中的str
Unicode 字符unicode
。但 Windows C 或 .NET 应用程序更可能使用 UTF-16,因为 Windows 原生 API 处理 UTF-16-LE 代码点WCHAR *
(也称为 16 位字串)。
那么,为什么是 4142?好吧,关键是什么并不重要。我猜一些程序员建议42。他的经理然后说:“这听起来不太安全。” 他叹了口气说:“我已经解释过为什么没有钥匙会比……你知道吗,算了吧,4142呢?” 经理说:“哦,这听起来很安全!” 所以这就是4142的原因。
如果它不是一个库函数,你能想出一个更好的方法来对这些值进行去混淆,而无需求助于神奇的 142 数字。
你确实需要使用魔法 4142,但你可以让它变得更简单:
def decrypt(block):
return struct.pack('>H', (4142 - int(block, 10)) % 65536)
因此,每个 5 个字符的块是 UTF-16 代码单元的十进制表示,使用 C 无符号短环绕规则从 4142 中减去。
这在本机 Windows C 中实现是微不足道的,但在 Python 中稍微难一些。我能想到的最好的转换函数是:
def decrypt_block(block):
return struct.pack('>H', (4142 - int(block, 10)) % 65536)
def decrypt(pwd):
blocks = [pwd[i:i+5] for i in range(0, len(pwd), 5)]
return ''.join(map(decrypt_block, blocks)).decode('utf-16-be')
这在 C 或 C# 中会更简单,这可能是他们实现的东西,所以让我解释一下我在做什么。
您已经知道如何将字符串转换为由 5 个字符组成的块序列。
Myint(block, 10)
正在做与您相同的事情int(block.lstrip('0'))
,确保'0'
前缀不会使 Python 将其视为八进制数字而不是十进制,但更明确。我认为这在 Jython 2.2 中实际上是不必要的(在更现代的 Python/Jython 中肯定不是),但我留下了它以防万一。
接下来,在 C 中,您只需执行unsigned short x = 4142U - y;
,它会自动适当地下溢。Python 没有unsigned short
值,只有 signed int
,所以我们必须手动进行下溢。(因为 Python 使用底除法和余数,符号总是与除数相同——这在 C 语言中不成立,至少在 C99 和大多数平台的 C89 中不成立。)
然后,在 C 中,我们只需将 unsigned short 转换为 16 位“宽字符”;Python 没有办法做到这一点,所以我们必须使用struct.pack
. (请注意,我将其转换为大端序,因为我认为这使调试更容易;在 C 中,您将转换为原生端序,并且由于这是 Windows,因此将是小端序。)
所以,现在我们得到了 2 个字符的 UTF-16-BE 代码点序列。我只是join
将它们变成一个大字符串,然后decode
将其作为 UTF-16-BE。
如果您真的想测试我是否正确,您需要找到不仅是非 ASCII 字符,而且是非西方字符。特别是,您需要:
- > U+4142 但 < U+10000 的字符。大多数 CJK 表意文字,如 U+7000 (瀀),都符合要求。这应该显示为
'41006'
,因为这是 4142-0x7000 翻转为无符号空头。
- >= U+10000 的字符。这包括不常见的 CJK 字符、专门的数学字符、来自古代文字的字符等。例如,旧斜体字符 U+10300 () 编码为代理对 (0xd800, 0xdf00);4142-0xd800=14382 和 4142-0xdf00=12590,所以你会得到
'1438212590'
.
第一个很难找到——即使是我接触过的大多数以中文和日文为母语的程序员都使用 ASCII 密码。第二个,更是如此;除了历史语言学教授之外,没有人会想到在他们的密码中使用古文字。根据墨菲定律,如果您编写了正确的代码,它将永远不会被使用,但如果您不这样做,它保证会在您发布代码时立即出现。