如何在Python中生成40/64位WEP密钥?

4
所以,我已经为这个问题苦苦思索了几个月,部分原因是这只是我的一个兴趣爱好,另外一部分原因是我不擅长编程。我搜索和研究了整个网络,但没有任何运气(除了一点小成功;见下文),所以我想我可以尝试向专家寻求帮助。
我正在尝试做的就是,如标题所示,根据“事实上”的标准从密码生成一个40/64位WEP密钥。(例如http://www.powerdog.com/wepkey.cgi网站会产生预期的输出)。我已经编写了部分脚本,可以将输入写入文件;其中一个输入将是小写密码。
很久以来,我不知道事实上的标准是什么,更不用说如何实现它了。最后我偶然发现了一篇文章(http://www.lava.net/~newsham/wlan/WEP_password_cracker.pdf)解释了这个问题(第18页有相关信息)。显然,密码是“通过XOR映射到32位值”,其结果然后用作“线性同余伪随机数生成器的种子(Python中有几个伪随机数生成器可以适合这个描述,我不知道),然后从该结果中取出几位。由于描述比较模糊,我不知道如何实现这一点。
我需要的是帮助在Python中编写生成器,并了解密钥是如何生成的。换句话说,我需要代码将“jackson”转换为“09F38AF593”。(请不要告诉我jackson = 09F38AF593; print (jackson))
我不是很擅长编程,所以也需要解释。
(是的,我知道WEP不安全。)

不清楚您想从这个问题中得到什么。您指向了实现该算法的JS源代码。从源代码(netpoint.com/wep.htm)中看,它是一个相当长且非常复杂的编码。为什么不花些时间尝试理解该实现,并询问SO有关您尝试理解代码时遇到的具体问题呢? - Stephen
我已经花了将近一个月的时间断断续续地尝试理解那个实现。作为一个新手,它几乎超出了我的能力范围。然而,我要说的是,我提供的第二个链接中的C程序似乎不需要如此复杂的设置。据我所读,生成64位WEP密钥时JS页面调用的唯一函数是ToHex和MM_FindObj。transform根本没有被使用。但是,我可能读错了。然而,我将尝试进一步理解,并在有具体问题时发布回复,而不是像之前那样胡乱猜测。 - Aktariel
顺便说一句,这个算法本身可以用4行C源代码实现。一旦你理解了它,它并不那么复杂。 - David Z
我不理解这个。 :D - Aktariel
2个回答

5

您在问题中提供的C代码非常有帮助;-) 无论如何,我已经将其翻译成Python。在阅读之前,让我说一句,我强烈鼓励您尝试自己编写代码,并仅使用我的转录作为指南。将算法从一种编程语言翻译到另一种通常是提高一种或两种语言技能的绝佳实践。即使您不懂C语言,只要您足够熟悉Python以编写程序,您应该能够理解C代码的要点,因为两者有很多相似之处。

好了,进入正题,以下是代码:

import itertools, operator

首先,伪随机数生成器在演示中被识别为线性同余生成器。这种类型的PRNG是一种通用算法,可以通过选择特定的acm值(维基百科文章中提到的变量)来“定制”。以下是一个通用线性同余生成器的实现:

def prng(x, a, c, m):
    while True:
        x = (a * x + c) % m
        yield x

希望你自己能够想到这一点。
现在进入实际功能:
def pass_to_key(passphrase):

该过程的第一步是将提供的口令进行哈希(或“映射”)为32位数字。 WEP算法通过创建一组4个字节(因此4 * 8 = 32位)并将其初始化为零来实现此目的。

    bits = [0,0,0,0]

该程序遍历字符串并将每个字符与其中一个字节进行异或运算;具体来说,第i个字符被异或到第i % 4个字节上。

    for i, c in enumerate(passphrase):
        bits[i & 3] ^= ord(c)

然后将这四个字节按顺序连接在一起,形成一个单独的32位值。(或者,我可以编写代码从一开始就将它们存储为32位数字)

    val = reduce(operator.__or__, (b << 8*i for (i,b) in enumerate(bits)))

这个32位的值被用作线性同余发生器的种子,具体数值可以在代码中看到。原始开发者是如何找到这些数字的,我不清楚。

    keys = []

线性同余生成器可以一次生成最多32位的输出。(在C语言中,这是数据类型的限制;在Python中,我不得不人为地强制执行它。)我需要20个字节来生成4个40位(5字节)WEP密钥,因此我将迭代PRNG 20次。

    for i, b in enumerate(itertools.islice(prng(val, 0x343fd, 0x269ec3, 1<<32), 20)):

从每个数字中只取右侧第三个字节(位16-23):

        keys.append((b >> 16) & 0xff)

为什么是第三个数?因为高位(从右边数第四个)的比特往往不会改变,低位的比特对于许多PRNG常量的值来说是可预测的。

之后,只需将生成的字节按5个一组打印出来即可。

    print ('%02x:%02x:%02x:%02x:%02x\n'*4) % tuple(keys)

谢谢您向我展示了在学习编程方面我还有多远要走的路(同时也感谢您提供的代码和解释)。我有一个快速的问题,您会如何将新的键值映射到变量并返回它,而不仅仅是打印输出呢? - Aktariel
@Aktariel:把这当做读者的练习吧;-) 根据你想要实现的方式,你可以考虑使用struct模块。 - David Z

1
我不确定那个网站所谈论的“事实标准”是什么,但我相当确定路由器制造商都会实现自己的方法。无论你如何做,只要相同的输入始终产生相同的输出;这是一种便利,使WEP用户可以记住一个短语而不是实际的十六进制密钥。即使是你发布的PDF中的方法也大多数含糊不清;它使用未定义的PRNG(每种类型的PRNG都会给出不同的结果),并从每个结果中取“一个字节”,而没有指定是哪个字节。如果你试图反向工程某个特定路由器的方法,请在帖子中提到,我们可能能够找出它是如何工作的,但并没有标准方法。

这个网站(http://www.netpoint.com/wep.htm)也能生成期望的结果,而且JavaScript是公开可见的。只是我不太懂JS,无法理解它。一个旧的Linux邮件列表帖子有一些C代码,可以生成所需的内容。(http://lists.linux-wlan.com/pipermail/linux-wlan-devel/2001-September/000597.html)同样的问题是,我对代码了解不够,无法理解它。根据我的研究,Linksys、Netgear、Belkin和DLink似乎都使用相同的算法,这似乎是某种标准。 - Aktariel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接