【破文作者】 rdsnow[BCG][PYG][D.4s]
【作者主页】 http://rdsnow.ys168.com
【 E-mail 】 rdsnow@163.com
【 作者QQ 】 83757177
【文章题目】 菜鸟学习《加密与解密》BlowFish笔记
【破解平台】 Win9x/NT/2000/XP/XP SP2
----------------------------------------------------------------------------------------------
【文章简介】
这两天跟踪某个软件,其中使用了变了形的BlowFish,虽然可以找到注册码的保存地址做出内存注册机,但是写不出算法注册机,心里很不平衡。以前从来没有接触过密码学知识,就从这个对称算法开始吧!
----------------------------------------------------------------------------------------------
【破解过程】
BlowFish是个64位分组的算法。输入64信息,运算后输出64位密文。
刚开始当然要找个简单的。找来找去,最后拿《加密与解密》光盘上的夜月的BlowFish's crackme keygen分析了。这个keygen对ID(待加密数据)进行了两次BlowFish的计算。
通过跟踪发现,对某个消息加密的过程,分两步进行:
1、密钥预处理过程:
6A 12 PUSH 12 ; /Arg2 = 00000012 压入密钥长度12
68 00304000 PUSH BFKEYGEN.00403000 ; |Arg1 = 00403000 压入密钥地址
E8 50FEFFFF CALL BFKEYGEN.004010C8 ; \BFKEYGEN.004010C8 密钥预处理
2、对信息加密过程:
68 CD404000 PUSH BFKEYGEN.004040CD ; /Arg2 = 004040CD 被加密信息的地址1
68 D1404000 PUSH BFKEYGEN.004040D1 ; |Arg1 = 004040D1 被加密信息的地址2
E8 E5FDFFFF CALL BFKEYGEN.0040106C ; \BFKEYGEN.0040106C BlowFish的核心函数BF_EN加密函数
先来认识一下BlowFish的两个盒,所谓盒子,我想就是两个数组:
PBOX盒:unsigned long PBOX[18],有18个元素的一维数组
unsigned long PBOX[4,256],总共有1024个元素的二维数组
这两个盒子里已经保存了一些固定的数值:
PBOX dd 0243f6a88h, 085a308d3h, 013198a2eh, 003707344h, 0a4093822h, 0299f31d0h
dd 0082efa98h, 0ec4e6c89h, 0452821e6h, 038d01377h, 0be5466cfh, 034e90c6ch
dd 0c0ac29b7h, 0c97c50ddh, 03f84d5b5h, 0b5470917h, 09216d5d9h, 08979fb1bh
SBOX1 dd 0d1310ba6h, 098dfb5ach, 02ffd72dbh, 0d01adfb7h, 0b8e1afedh, 06a267e96h
dd 0ba7c9045h, 0f12c7f99h, 024a19947h, 0b3916cf7h, 00801f2e2h, 0858efc16h
dd 0636920d8h, 071574e69h, 0a458fea3h, …………(因为很多所以就省略了)
SBOX2 ………………
SBOX3 ………………
SBOX4 ………………
MD5编码过程中只要用到4个常数,而BlowFish却要用到这么多常数。现在很多的算法search工具,就是通过搜索SBOX来判断程序使用了什么算法的。
★★★★★★★★★★★★过程1、密钥的预处理★★★★★★★★★★★★
6A 12 PUSH 12 ; /Arg2 = 00000012 压入密钥长度12
68 00304000 PUSH BFKEYGEN.00403000 ; |Arg1 = 00403000 压入密钥地址
E8 50FEFFFF CALL BFKEYGEN.004010C8 ; \BFKEYGEN.004010C8 密钥预处理
跟进CALL BFKEYGEN.004010C8
004010C8 /$ 55 PUSH EBP
004010C9 |. 8BEC MOV EBP,ESP
004010CB |. 83C4 F4 ADD ESP,-0C
004010CE |. 53 PUSH EBX
004010CF |. 57 PUSH EDI
004010D0 |. 56 PUSH ESI
004010D1 |. 52 PUSH EDX
004010D2 |. 51 PUSH ECX
004010D3 |. BE 3D304000 MOV ESI,BFKEYGEN.0040303D ; KEY_PBOX地址(PBOX处理后放到这里)
004010D8 |. B8 1D414000 MOV EAX,BFKEYGEN.0040411D ; SBOX地址
004010DD |. 8D4E 48 LEA ECX,DWORD PTR DS:[ESI+48] ; KEY_SBOX地址(SBOX处理后放到这里)
004010E0 |> BA 00010000 /MOV EDX,100 ; 开始循环
004010E5 |> 8B38 |/MOV EDI,DWORD PTR DS:[EAX] ; 取SBOX内容
004010E7 |. 83C0 04 ||ADD EAX,4
004010EA |. 8939 ||MOV DWORD PTR DS:[ECX],EDI ; 填充到KEY_SBOX
004010EC |. 83C1 04 ||ADD ECX,4
004010EF |. 4A ||DEC EDX
004010F0 |.^ 75 F3 |\JNZ SHORT BFKEYGEN.004010E5
004010F2 |. 3D 1D514000 |CMP EAX,BFKEYGEN.0040511D ; 判断有没有填充完毕
004010F7 |.^ 7C E7 \JL SHORT BFKEYGEN.004010E0 ; 循环出口
以上循环结束了,这个循环就是SBOX的1024个元素搬新家,搬到KEY_SBOX
004010F9 |. 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] ; 取用户密钥KEY的地址
004010FC |. BF D5404000 MOV EDI,BFKEYGEN.004040D5
00401101 |. 33C0 XOR EAX,EAX
00401103 |. 2BFE SUB EDI,ESI
00401105 |. C745 FC 1200000>MOV DWORD PTR SS:[EBP-4],12 ; 置循环次数为18(PBOX内有18个元素)
0040110C |> 33C9 /XOR ECX,ECX ; 进入循环
0040110E |. C745 F8 0400000>|MOV DWORD PTR SS:[EBP-8],4
00401115 |> 33DB |/XOR EBX,EBX
00401117 |. 8A1C02 ||MOV BL,BYTE PTR DS:[EDX+EAX]
0040111A |. C1E1 08 ||SHL ECX,8
0040111D |. 0BCB ||OR ECX,EBX
0040111F |. 40 ||INC EAX
00401120 |. 3B45 0C ||CMP EAX,DWORD PTR SS:[EBP+C]
00401123 |. 7C 02 ||JL SHORT BFKEYGEN.00401127
00401125 |. 33C0 ||XOR EAX,EAX
00401127 |> 8B5D F8 ||MOV EBX,DWORD PTR SS:[EBP-8]
0040112A |. 4B ||DEC EBX
0040112B |. 895D F8 ||MOV DWORD PTR SS:[EBP-8],EBX
0040112E |.^ 75 E5 |\JNZ SHORT BFKEYGEN.00401115 ; 这里嵌套了个小循环,作用是用KEY中依次取4个字符的ASC,如果KEY取完,就从KEY的头个字母继续取
00401130 |. 8B1C3E |MOV EBX,DWORD PTR DS:[ESI+EDI]
00401133 |. 83C6 04 |ADD ESI,4
00401136 |. 33D9 |XOR EBX,ECX ; 用取出的4个字符的ASC XOR PBOX
00401138 |. 8B4D FC |MOV ECX,DWORD PTR SS:[EBP-4]
0040113B |. 895E FC |MOV DWORD PTR DS:[ESI-4],EBX ; 用异或结果填充KEY_PBOX
0040113E |. 49 |DEC ECX ; 计数器减1
0040113F |. 894D FC |MOV DWORD PTR SS:[EBP-4],ECX
00401142 |.^ 75 C8 \JNZ SHORT BFKEYGEN.0040110C ; 循环出口
上面循环作用是:依次取KEY的字符的ASC异或PBOX,KEY可以循环使用,结果保存在KEY_PBOX中。
接下来要使用BlowFish的核心函数BF_EN来处理KEY_PBOX了,这个BF_EN函数就是后来用来加密输入信息的。被加密的64位信息被分成32位的两部分,XL和XR
PUSH BFKEYGEN.004040CD ; /Arg2 = 004040CD XR的地址
PUSH BFKEYGEN.004040D1 ; |Arg1 = 004040D1 XL的地址
CALL BFKEYGEN.0040106C ; \BFKEYGEN.0040106C BlowFish的核心函数BF_EN加密函数
输出信息保存在[4040C5]和[4040D1]两个地址中。
00401144 |. BB 3D304000 MOV EBX,BFKEYGEN.0040303D ; KEY_PBOX地址
00401149 |. 33C0 XOR EAX,EAX
0040114B |. A3 C5404000 MOV DWORD PTR DS:[4040C5],EAX ; [4040C5]保存了XR,先置0
00401150 |. A3 C9404000 MOV DWORD PTR DS:[4040C9],EAX ; [4040C9]保存了XL,先置0
00401155 |. 8BF3 MOV ESI,EBX
00401157 |. BF 09000000 MOV EDI,9 ; 循环次数置9
0040115C |> 8D05 C5404000 /LEA EAX,DWORD PTR DS:[4040C5] ; 进入循环
00401162 |. 8D0D C9404000 |LEA ECX,DWORD PTR DS:[4040C9]
00401168 |. 50 |PUSH EAX ; /Arg2 => 004040C5
00401169 |. 51 |PUSH ECX ; |Arg1 => 004040C9
0040116A |. E8 FDFEFFFF |CALL BFKEYGEN.0040106C ; \核心加密函数:BF_EN
0040116F |. A1 C9404000 |MOV EAX,DWORD PTR DS:[4040C9]
00401174 |. 8B0D C5404000 |MOV ECX,DWORD PTR DS:[4040C5] ; 取出BF_EN结果
0040117A |. 8906 |MOV DWORD PTR DS:[ESI],EAX
0040117C |. 894E 04 |MOV DWORD PTR DS:[ESI+4],ECX ; 替换KEY_PBOX的数据
0040117F |. 83C6 08 |ADD ESI,8
00401182 |. 4F |DEC EDI
00401183 |.^ 75 D7 \JNZ SHORT BFKEYGEN.0040115C ; 循环出口
这个循环大致是这样的:
BF_EN (0x0000000000000000) 结果填入KEY_BOX[1]KEY_BOX[2]
BF_EN(KEY_BOX[1]KEY_BOX[2]) 结果填入KEY_BOX[3]KEY_BOX[4]
BF_EN(KEY_BOX[3]KEY_BOX[4]) 结果填入KEY_BOX[5]KEY_BOX[6]
……………………
BF_EN(KEY_BOX[15]KEY_BOX[16]) 结果填入KEY_BOX[17]KEY_BOX[18]
BF_EN(KEY_BOX[17]KEY_BOX[18]) 结果填入KEY_BOX[1]KEY_BOX[2]
00401185 |. 8D73 4C LEA ESI,DWORD PTR DS:[EBX+4C] ; ESI-4是KEYSBOX的地址
00401188 |. C745 F4 0400000>MOV DWORD PTR SS:[EBP-C],4
0040118F |> BF 80000000 /MOV EDI,80
00401194 |> 8D0D C5404000 |/LEA ECX,DWORD PTR DS:[4040C5] ; 这里循环前保存了对KEY_PBOX处理的结果
0040119A |. 8D15 C9404000 ||LEA EDX,DWORD PTR DS:[4040C9] ; 这里循环前保存了对KEY_PBOX处理的结果
004011A0 |. 51 ||PUSH ECX ; /Arg2 => 004040C5
004011A1 |. 52 ||PUSH EDX ; |Arg1 => 004040C9
004011A2 |. E8 C5FEFFFF ||CALL BFKEYGEN.0040106C ; \BFKEYGEN.0040106C
004011A7 |. 8B0D C9404000 ||MOV ECX,DWORD PTR DS:[4040C9]
004011AD |. 8B15 C5404000 ||MOV EDX,DWORD PTR DS:[4040C5]
004011B3 |. 894E FC ||MOV DWORD PTR DS:[ESI-4],ECX
004011B6 |. 8916 ||MOV DWORD PTR DS:[ESI],EDX
004011B8 |. 83C6 08 ||ADD ESI,8
004011BB |. 4F ||DEC EDI
004011BC |.^ 75 D6 |\JNZ SHORT BFKEYGEN.00401194
004011BE |. FF4D F4 |DEC DWORD PTR SS:[EBP-C]
004011C1 |.^ 75 CC \JNZ SHORT BFKEYGEN.0040118F
这个循环用了同样的方法处理KEY_SBOX,不同的是第一次不是处理一个全0的64位信息,而是上一个循环对KEY_PBOX[17]和KEY_PBOX[28]的结果。
004011C3 |. 59 POP ECX
004011C4 |. 5A POP EDX
004011C5 |. 5E POP ESI
004011C6 |. 5F POP EDI
004011C7 |. 5B POP EBX
004011C8 |. C9 LEAVE
004011C9 \. C2 0800 RETN 8
那个BF_EN函数到底做了什么呢,现在不跟了,到下面过程2再来看吧!
★★★★★★★★★★★★过程2、信息加密★★★★★★★★★★★★
此时,已经完成了KEY_PBOX[18]和对KEY_SBOX[4,256]的处理,开始对信息编码。所用的就是初始化过程中用到的BF_EN函数。
被加密的64位信息被分成32位的两部分,XL和XR
PUSH BFKEYGEN.004040CD ; /Arg2 = 004040CD XR的地址
PUSH BFKEYGEN.004040D1 ; |Arg1 = 004040D1 XL的地址
CALL BFKEYGEN.0040106C ; \BFKEYGEN.0040106C BlowFish的核心函数BF_EN加密函数
输出信息保存在[4040C5]和[4040D1]两个地址中。
0040106C /$ 55 PUSH EBP
0040106D |. 8BEC MOV EBP,ESP
0040106F |. 83C4 FC ADD ESP,-4
00401072 |. 53 PUSH EBX
00401073 |. 57 PUSH EDI
00401074 |. 56 PUSH ESI
00401075 |. 52 PUSH EDX
00401076 |. 51 PUSH ECX
00401077 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; 取XL的地址
0040107A |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C] ; 取XR的地址
0040107D |. 8B00 MOV EAX,DWORD PTR DS:[EAX] ; 取XL
0040107F |. 8B31 MOV ESI,DWORD PTR DS:[ECX] ; 取XR
00401081 |. BF 3D304000 MOV EDI,BFKEYGEN.0040303D ; 取KEY_PBOX的地址
00401086 |. C745 FC 1000000>MOV DWORD PTR SS:[EBP-4],10 ; 循环次数置16
0040108D |. 8BDF MOV EBX,EDI
0040108F |> 3303 /XOR EAX,DWORD PTR DS:[EBX] ; XL = XL ^ KEY_PBOX[i]
00401091 |. 8BD0 |MOV EDX,EAX ; 保存异或结果
00401093 |. 50 |PUSH EAX
00401094 |. E8 67FFFFFF |CALL BFKEYGEN.00401000 ; XL = F(XL),F( )另外一个函数
00401099 |. 8B4D FC |MOV ECX,DWORD PTR SS:[EBP-4] ; 取循环次数
0040109C |. 33C6 |XOR EAX,ESI ; XL = XL ^ XR
0040109E |. 83C3 04 |ADD EBX,4
004010A1 |. 49 |DEC ECX ; 循环次数减1
004010A2 |. 8BF2 |MOV ESI,EDX ; 上面保存的异或结果赋给XR
004010A4 |. 894D FC |MOV DWORD PTR SS:[EBP-4],ECX ; 保存循环次数
004010A7 |.^ 75 E6 \JNZ SHORT BFKEYGEN.0040108F ; 循环出口
004010A9 |. 8B4F 40 MOV ECX,DWORD PTR DS:[EDI+40] ; 取KEY_PBOX[17]
004010AC |. 8B57 44 MOV EDX,DWORD PTR DS:[EDI+44] ; 取KEY_PBOX[18]
004010AF |. 33C8 XOR ECX,EAX ; XL ^ KEY_PBOX[18]
004010B1 |. 33D6 XOR EDX,ESI ; XR ^ KEY_PBOX[17]
004010B3 |. 8915 C9404000 MOV DWORD PTR DS:[4040C9],EDX ; 输出XL
004010B9 |. 890D C5404000 MOV DWORD PTR DS:[4040C5],ECX ; 输出XR
004010BF |. 59 POP ECX
004010C0 |. 5A POP EDX
004010C1 |. 5E POP ESI
004010C2 |. 5F POP EDI
004010C3 |. 5B POP EBX
004010C4 |. C9 LEAVE
004010C5 \. C2 0800 RETN 8
进入F( )函数分析,F( )是用它来处理XL的,进去看看:
00401000 /$ 55 PUSH EBP
00401001 |. 8BEC MOV EBP,ESP
00401003 |. 53 PUSH EBX
00401004 |. 57 PUSH EDI
00401005 |. 56 PUSH ESI
00401006 |. 52 PUSH EDX
00401007 |. 51 PUSH ECX
00401008 |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
0040100B |. 8AC1 MOV AL,CL
0040100D |. 25 FF000000 AND EAX,0FF
00401012 |. C1E9 08 SHR ECX,8
00401015 |. 8BD0 MOV EDX,EAX
00401017 |. 8AC1 MOV AL,CL
00401019 |. BF 3D304000 MOV EDI,BFKEYGEN.0040303D
0040101E |. 25 FF000000 AND EAX,0FF
00401023 |. C1E9 08 SHR ECX,8
00401026 |. 8BF0 MOV ESI,EAX
00401028 |. 8BC1 MOV EAX,ECX
0040102A |. C1E8 08 SHR EAX,8
0040102D |. 25 FF000000 AND EAX,0FF ; a
00401032 |. 81E1 FF000000 AND ECX,0FF ; b
00401038 |. 81E6 FFFF0000 AND ESI,0FFFF ; c
0040103E |. 81E2 FFFF0000 AND EDX,0FFFF ; d
上面代码就是把XL分成4个8位的a、b、c、d
00401044 |. 8B4487 48 MOV EAX,DWORD PTR DS:[EDI+EAX*4+48] ; 取出KEY_SBOX[1,a]
00401048 |. 8B9C8F 48040000 MOV EBX,DWORD PTR DS:[EDI+ECX*4+448] ; 取出KEY_SBOX[2,b]
0040104F |. 8B8CB7 48080000 MOV ECX,DWORD PTR DS:[EDI+ESI*4+848] ; 取出KEY_SBOX[3,c]
00401056 |. 03C3 ADD EAX,EBX ; KEY_SBOX[1,a]+KEY_SBOX[2,b]
00401058 |. 33C1 XOR EAX,ECX ; 结果 ^ KEY_SBOX[3,c]
0040105A |. 8B8C97 480C0000 MOV ECX,DWORD PTR DS:[EDI+EDX*4+C48] ; 取出KEY_SBOX[4,d]
00401061 |. 03C1 ADD EAX,ECX ; 结果 ^ KEY_SBOX[4,d]
00401063 |. 59 POP ECX
00401064 |. 5A POP EDX
00401065 |. 5E POP ESI
00401066 |. 5F POP EDI
00401067 |. 5B POP EBX
00401068 |. C9 LEAVE
00401069 \. C2 0400 RETN 4
a、b、c、d作为二维数组的下标到KEY_SBOX里取元素来运算
就是:(((KEY_SBOX[1,a]+KEY_SBOX[2,b]) ^ KEY_SBOX[3,c] ) + KEY_SBOX[4,d]
但是写注册机时要注意高位溢出的问题
信息解密时密钥预处理的过程与加密时完全相同,信息解密的过程就是把信息加密过程的key_pbox逆序使用即可。
----------------------------------------------------------------------------------------------
【破解心得】
共享软件为了不让破解者找到KEY,常常把密钥的预处理过程放到程序的其他地方,只要找到SBOX盒子所在的地址(Peid的插件就具有这个功能),下内存断点,就能来到密钥预处理的地方,从而找到KEY。
对于算法,我是外行,第一次接触密码学知识,理解还是很肤浅,写的也没有什么新的东西,只是把我得学习过程跟大家分享一下。现在越来越多的程序使用变形代码来阻止写算法注册机了,真希望自己能够有所进步。
----------------------------------------------------------------------------------------------
【破解声明】 我是一只小菜鸟,偶得一点心得,愿与大家分享:)
【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!
----------------------------------------------------------------------------------------------
文章写于2005-8-24 18:42:39