首页
社区
课程
招聘
[原创]密码学CrackMe之RSA512算法CM分析
发表于: 2007-9-12 16:47 7253

[原创]密码学CrackMe之RSA512算法CM分析

2007-9-12 16:47
7253

【文章标题】: 'RSACrackMe512分析过程及汇编注册机源码
【文章作者】: coolstar14
【使用工具】: IDA, BigInterCalc, RDLP
【软件名称】: RSACrackMe512
【软件大小】: 40KB
【下载地址】: http://bbs.pediy.com/attachment.php?attachmentid=7447&d=1187671766
【软件介绍】: 用RSA512作为校验算法的CrackMe。
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
详细过程:
IDA反汇编, 字符串参考, 直接定位到关键代码:
.text:00402F18
.text:00402F18 loc_402F18:                             ; CODE XREF: DialogFunc+140j
.text:00402F18                 lea     eax, [ebp+dst_buffer]
.text:00402F1E                 lea     ecx, [ebp+rsa_n]
.text:00402F24                 push    eax             ; dst_buffer
.text:00402F25                 push    ecx             ; rsa_n
.text:00402F26                 lea     edx, [ebp+input_sn]
.text:00402F2C                 push    offset s_10001  ; "10001"
.text:00402F31                 push    edx             ; input_sn
.text:00402F32                 mov     [ebp+var_4], 0
.text:00402F39                 call    sub_402BC0                        ;关键函数, 跟进
.text:00402F39
.text:00402F3E                 test    eax, eax
.text:00402F40                 jnz     short loc_402F68                ; eax不等于0, 跳转到后续判断, 否则就提示失败
.text:00402F40
.text:00402F42                 push    40h             ; uType
.text:00402F44                 push    offset Caption  ; "注册提示"
.text:00402F44
.text:00402F49
.text:00402F49 loc_402F49:                             ; CODE XREF: DialogFunc+1D8j        ;注册提示
.text:00402F49                 push    offset s_VSIAGm ; "注册码错误,继续加油!"
                                        ....
.text:00402F64                 mov     esp, ebp
.text:00402F66                 pop     ebp
.text:00402F67                 retn
.text:00402F67
.text:00402F68 ; ---------------------------------------------------------------------------
.text:00402F68
.text:00402F68 loc_402F68:                             ; CODE XREF: DialogFunc+190j
.text:00402F68                 lea     eax, [ebp+dst_buffer]
.text:00402F6E                 lea     ecx, [ebp+String2]
.text:00402F74                 push    eax             ; lpString1
.text:00402F75                 push    ecx             ; lpString2
.text:00402F76                 call    sub_402CE0                        ;比较函数, 可以通过内存查看, eax, ecx分别为解密sn后获得的缓冲区和我们输入的用户名
.text:00402F76
.text:00402F7B                 add     esp, 8
.text:00402F7E                 cmp     eax, 1
.text:00402F81                 push    40h             ; uType
.text:00402F83                 push    offset Caption  ; "注册提示"
.text:00402F88                 jnz     short loc_402F49                ;前面比较函数返回值 eax不等于1跳转到失败, 否则注册成功.
.text:00402F88
.text:00402F8A                 push    offset s_ZUgmVSI ; "恭喜你,注册码正确!"
.text:00402F8F                 push    esi             ; hWnd
.text:00402F90                 call    ds:MessageBoxA
.text:00402F96                 xor     eax, eax

函数 402F39, 参数4个, 通过查看内存可以确定, 有一个为我们输入的序列号, 一个为10001的字符串, 它是RSA公钥对中最常用的e , 还有一个长度为128的字符串, 这个很容易就可以怀疑它是RSA公钥对中的n. 另外一个会在下面的比较函数中用它, 它就是结果输出缓冲区了.

函数:sub_402BC0
.text:00402BC0 ; Attributes: bp-based frame
.text:00402BC0
.text:00402BC0 ; int __stdcall sub_402BC0(int input_sn,int rsa_e_10001,int rsa_n,LPSTR dst_buffer)
.text:00402BC0 sub_402BC0      proc near               ; CODE XREF: DialogFunc+189p
.text:00402BC0
.text:00402BC0 String2         = byte ptr -2088h
.text:00402BC0 var_88          = dword ptr -88h
.text:00402BC0 var_big_e       = dword ptr -64h
.text:00402BC0 var_big_n       = dword ptr -44h
.text:00402BC0 var_big_sn      = dword ptr -20h
.text:00402BC0 var_10          = dword ptr -10h
.text:00402BC0 var_C           = dword ptr -0Ch
.text:00402BC0 var_4           = dword ptr -4
.text:00402BC0 input_sn        = dword ptr  8
.text:00402BC0 rsa_e_10001     = dword ptr  0Ch
.text:00402BC0 rsa_n           = dword ptr  10h
.text:00402BC0 dst_buffer      = dword ptr  14h
.text:00402BC0
.text:00402BC0                 push    ebp
.text:00402BC1                 mov     ebp, esp
.text:00402BC3                 push    0FFFFFFFFh
.text:00402BC5                 push    offset loc_406973
.text:00402BCA                 mov     eax, large fs:0
.text:00402BD0                 push    eax
.text:00402BD1                 mov     large fs:0, esp
.text:00402BD8                 push    ecx
.text:00402BD9                 mov     eax, 2078h
.text:00402BDE                 call    sub_4034A0
.text:00402BDE
.text:00402BE3                 push    ebx
.text:00402BE4                 push    esi
.text:00402BE5                 push    edi
.text:00402BE6                 lea     ecx, [ebp+var_88]
.text:00402BEC                 mov     [ebp+var_10], esp
.text:00402BEF                 call    sub_402900
.text:00402BEF
.text:00402BF4                 xor     ebx, ebx
.text:00402BF6                 lea     ecx, [ebp+var_big_sn]
.text:00402BF9                 mov     [ebp+var_4], ebx
.text:00402BFC                 call    sub_401060
.text:00402BFC
.text:00402C01                 mov     eax, [ebp+rsa_e_10001]
.text:00402C04                 lea     ecx, [ebp+var_big_e]
.text:00402C07                 push    eax
.text:00402C08                 mov     byte ptr [ebp+var_4], 2
.text:00402C0C                 call    sub_4028F0                                ; 大数转换, 只有一个参数为大数的字符串表示. 之前有几个函数调用, 像是初始化之类的东西, 具体没弄明白.
.text:00402C0C
.text:00402C11                 mov     ecx, [ebp+rsa_n]
.text:00402C14                 push    ecx
.text:00402C15                 lea     ecx, [ebp+var_big_n]
.text:00402C18                 call    sub_4028F0
.text:00402C18
.text:00402C1D                 mov     edx, [ebp+input_sn]
.text:00402C20                 lea     ecx, [ebp+var_big_sn]
.text:00402C23                 push    edx
.text:00402C24                 call    sub_4028F0
.text:00402C24
.text:00402C29                 mov     ecx, 800h
.text:00402C2E                 xor     eax, eax
.text:00402C30                 lea     edi, [ebp+String2]
.text:00402C36                 rep stosd
.text:00402C38                 lea     eax, [ebp+var_big_sn]
.text:00402C3B                 lea     ecx, [ebp+String2]
.text:00402C41                 push    eax
.text:00402C42                 push    2000h
.text:00402C47                 push    ecx
.text:00402C48                 lea     ecx, [ebp+var_88]
.text:00402C4E                 call    sub_402A40                                ;大数转换完, 下一步自然是计算, 这个函数很麻烦, 需要跟进, 验证序列号有一部分在这里面.
.text:00402C4E
.text:00402C53                 mov     eax, [ebp+dst_buffer]
.text:00402C56                 lea     edx, [ebp+String2]
.text:00402C5C                 push    edx             ; lpString2
.text:00402C5D                 push    eax             ; lpString1
.text:00402C5E                 call    ds:lstrcpyA                                ;将结果复制到目的缓冲区, 可以由这儿中断, 然后直接修改内存使eax指向内在为我们输入的用户名, 继续后可以看到, 会提示注册成功, 所以后面调用的函数我们就可以不用关心了.
.text:00402C64                 lea     ecx, [ebp+var_big_sn]
                                        ...............
.text:00402C96
.text:00402C96 sub_402BC0      endp

IDA有个很实用的功能, 它可以让你修改参数名字, 它会在整个函数体里统一替换, 在自己确定了某个参数和变量的含义后可以修改它为好记的名字, 方便之后的阅读和理解. 上面的函数的参数为我跟踪替换后的样子,比最初版本肯定是要好些的.
回到分析上, 在大数的处理上, 不清楚这个CM用的什么大数库或者为自己写的吧. 上面瓢的大数的参数, 比如说var_big_e, 是个内存地址, 其+4后的地址指向malloc出来存放转换出来大数的地址.

函数:sub_402A40 其中
.text:00402A78
.text:00402A7D                 lea     edi, [esi+44h]
.text:00402A80                 add     esi, 24h
.text:00402A83                 push    edi             ; big_n
.text:00402A84                 push    esi             ; big_e_10001
.text:00402A85                 lea     ecx, [esp+38h+var_AfterDeBuff]
.text:00402A89                 push    ebx             ; big_sn
.text:00402A8A                 push    ecx             ; out is powmod=sn^10001 (mod n)
.text:00402A8B                 call    sub_402670
通过查看 402670 调用前后各参数地址的变化, 发现只有ecx指向的缓冲区有变化, 使用 BigIntCalc代入各参数计算, 发现ecx的结果恰巧是powmod操作. 由此可以确定第一步, 这个cm是把序列号转为大数, 用公钥解密了的. 那么没有私钥好像是不能继续下去了, 分解512 好像很花时间, 我们采用替换公钥对n的方法继续下面的分析, IDA 查看字符串参数, 找到公钥串所在偏移 4080e0, 相对文件偏移80e0(ida可以在状态栏看到的), Ultraedit打开exe, 直接定位并使用我们自己产生的公钥对中的n替换它. 这样我们就可得到一个知道私钥的修改版本.

重新截入修改后的版本, 在402a8b下断, 使用私钥加密我们输入的用户名, 加密后的结果做为序列号输入, 点击注册, 断下, 单步运行一下, 可以看到ecx指向的大数, 加密前的用户名的逆序. ecx为指针, 它指向的大数实际地址为其地址+4, 然后再做为地址, 如:
0012d508 14 71 40 00 70 05 8f 00 则其指向的大数实际为8f0570, 查看这个地址, 可以看到调用402670前为我们输入的序列号, 调用后变为了我们输入的用户名的逆序. 本来以为这样应该算是注册了, 结果运行仍然弹出注册失败. 看来并不是简单的用户名私钥加密形成序列号. 继续..

mov     eax, [esp+30h+arg_2000]
lea     edx, [esp+30h+var_AfterDeBuff]
push    2
push    edx             ; addr after powmod
push    eax
push    ecx             ; addr String2
call    sub_402B10
402b10, 它有个参数为我们期望的外层比较函数将要使用的地址, 跟进它.

402b10 调用 4024a0, 这个函数对解密后的数据做了进一步的判断, 如果不符合要求, 就返回一个 负数, 解密出来的内容并不会复制到我们期望的String2的地址中, 这个函数我没分析出来, 后来在密码学CM主帖中看到一个RSA1024有效的注册码, RSA1024那个CM与512这个反汇编后除了公钥n长度不一致外其它倒是一样. 所以看到powmod后的地址的结果, 从而猜测出解密后数据应该的内容继而得出4024a0函数各参数的意义和作用.
4024a0, 它判断了解密后数据的长度, 然后符合长度限制的它复制了指定偏移指定位的数据到目的地址. 其4个参数分别为:
源地址, 目的地址, 偏移位置, 复制字节数.
402b10 3次调用4024a0, 分别做了如下操作, 1复制并检查powmod解密后数据倒数第二个字节为2, 2复制获得powmod解密后数据倒数3,4字节做为有效数据长度xlen, 3以第二步取到长度复制偏移0长度xlen的数据到目的缓冲区.

最后, 可以看到sn产生的办法, 取这样一个串, user 256-4-ulen的任意字符 两字节ulen 02 任意结尾. 最后将这个串逆序 得到 加密前原文, 该串以私钥加密即得到序列号. 因为填充字符的存在, 所以相同用户名是可以有很多匹配的序列号的.

结束:
验证用工具使用了 readyu 大侠提供的, RDLP, BigInterCalc.
下载位置:
http://bbs1.pediy.com/showthread.php?t=47934&highlight=RDLP
http://bbs1.pediy.com/showthread.php?t=49005&highlight=RDLP

附一组修改公钥n后验证成功的注册码
替换的公钥n:
C1E8FDD98735103BCCEC2032A319FB8ADB1E79D5663486116CF54EE96CE3CB8988974330DEBB3639DC41C0FC0680F0549D94E90410D1B01A58E9D0F265008FD9
加密用私钥d:
41A1C0B9EDBF921D0B81286CBB33C225FF8053305D858D933C53D33FC2B15F6437E7D80333B01EE6DF23D3BF378F6FAAF366BC5398CF9E74B4E6836174894A59

加密前源串:
01020a00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFeeFFFFFFFFFFFFFFFFFF00636F6F6C737461723134

用户名:coolstar14
序列号:5BC6E01B6C7D34455FDB584A6B5352187CF9ECCB29FD28E4FFFF3FB33D4A6B27191285FFB23451FA9DDA95EA44909D2B9D54BBC77841F668CD9F6F3BC266131F


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (5)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
严重支持一下lz
  
2007-9-12 22:47
0
雪    币: 2256
活跃值: (941)
能力值: (RANK:2210 )
在线值:
发帖
回帖
粉丝
3
分析的很透彻
好文
2007-9-16 12:41
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持,收藏先
2007-9-20 16:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
有时间要好好研读一下.
2007-9-22 21:14
0
雪    币: 1022
活跃值: (31)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
汇编注册机源码怎么没找到!!!
要是哪位兄弟有RSA的汇编源码(C的我有)能共享,小弟感激不尽!!!
ling---yu@sohu.com
2008-3-19 22:35
0
游客
登录 | 注册 方可回帖
返回
//