分析 CM 源码,并追出注册码

这是一个 crackme 程序,crackme 一般是开发者为软件安全单独设计的一个防破解校验程序。供大家测试软件安全性的作品。 也是为提高逆向爱好者的逆向水平
作者本来放成品 要求大家追码, 追码的意思就是跟踪注册码,但是他不小心放了源码出来,然后我分析了一波,成功把注册码分析出来,并且发帖做下了笔记
以下是本人在吾爱破解首发原帖分析内容,特此转发在柠檬班供大家了解 windows 桌面应用的安全测试(调试)
程序是易语言编写的,经典的不是用了什么语言,经典的是算法。
这个是 CrazyNut 大佬 https://www.52pojie.cn/thread-828649-1-1.html 成品地址。要玩的可以自行去下载,注册码我猜你没得什么好测试的吧。
https://www.52pojie.cn/thread-828808-1-1.html 这是我的原帖地址
只要不是 Web 自动化,APP 自动化我都默认传到接口自动化这一栏😁

提供一组注册码,也是唯一的一组
-0-0 0-0 0- --00 -0-- -0 00- -

效果截图
image.png

前沿

这个 CM 是 CrazyNut 大佬传的一个悬赏 200CB 的成品,结果他传成了源码,
一开始我也没注意到,没打算打开看的。wgz001 大佬说是一个源码,仔细看了以下附件大小为 2K
易语言的,感兴趣下载下来分析一波。毕竟有些年头没研究有技术的东西了。当然这个也不算技术
我这个垃圾别人都用 OD 逆向,我就用源码逆向吧,哈哈,源码能叫逆向么?
我把源码贴出来了,但是不传源码让你们调试,你们要学习最好用 OD 分析。
这是一个很简单分析的帖子。
由于 CrazyNut 大佬的帖子还在,我就不把码子贴出来了。

分析源码是一件非常有趣的事情
也不妨一些大佬,多发一些 cm 源码 ,让我们这些菜鸟分析一下算法
以下是源码,排版可能不会太好。

易语言CM-欢迎各位大佬 - 『UnPackMe◇CrackMe◇KeyGenMe◇ReverseMe』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

源码

[mw_shl_code=erlang,true].版本 2
.支持库 spec

.程序集 窗口程序集_启动窗口

.子程序 _验证_被单击


' 调试输出 (ToZM (编辑框1.内容))
JPDA (真)


.子程序 ToZM, 文本型
.参数 DM, 文本型

调试输出 (到文本 ({ 45, 48, 45, 48 }))  ' c
调试输出 (到文本 ({ 48, 45, 48 }))  ' r
调试输出 (到文本 (到文本 ({ 48, 45 })))  ' a
调试输出 (到文本 ({ 45, 45, 48, 48 }))  ' z
调试输出 (到文本 ({ 45, 48, 45, 45 }))  ' y
调试输出 (到文本 ({ 45, 48 }))  ' n
调试输出 (到文本 ({ 48, 48, 45 }))  ' u
调试输出 (到文本 ({ 45 }))  ' t
' CRAZYNUT
.判断开始 (DM = 到文本 ({ 48, 45 }))
    返回 (到文本 ({ 65 }))  ' A

.判断 (DM = 到文本 ({ 45, 48, 48, 48 }))
    返回 (到文本 ({ 66 }))  ' B

.判断 (DM = 到文本 ({ 45, 48, 45, 48 }))
    返回 (到文本 ({ 67 }))  ' C

.判断 (DM = 到文本 ({ 45, 48, 48 }))
    返回 (到文本 ({ 68 }))  ' D

.判断 (DM = 到文本 ({ 48 }))
    返回 (到文本 ({ 69 }))  ' E

.判断 (DM = 到文本 ({ 48, 48, 45, 48 }))
    返回 (到文本 ({ 70 }))  ' F

.判断 (DM = 到文本 ({ 45, 45, 48 }))
    返回 (到文本 ({ 71 }))  ' G

.判断 (DM = 到文本 ({ 48, 48, 48, 48 }))
    返回 (到文本 ({ 72 }))  ' H

.判断 (DM = 到文本 ({ 48, 48 }))
    返回 (到文本 ({ 73 }))  ' I

.判断 (DM = 到文本 ({ 48, 45, 45, 45 }))
    返回 (到文本 ({ 74 }))  ' J

.判断 (DM = 到文本 ({ 45, 48, 45 }))
    返回 (到文本 ({ 75 }))  ' K

.判断 (DM = 到文本 ({ 48, 45, 48, 48 }))
    返回 (到文本 ({ 76 }))  ' L

.判断 (DM = 到文本 ({ 45, 45 }))
    返回 (到文本 ({ 77 }))  ' M

.判断 (DM = 到文本 ({ 45, 48 }))
    返回 (到文本 ({ 78 }))  ' N

.判断 (DM = 到文本 ({ 45, 45, 45 }))
    返回 (到文本 ({ 79 }))  ' O

.判断 (DM = 到文本 ({ 48, 45, 45, 48 }))
    返回 (到文本 ({ 80 }))  ' P

.判断 (DM = 到文本 ({ 45, 45, 48, 45 }))
    返回 (到文本 ({ 81 }))  ' Q

.判断 (DM = 到文本 ({ 48, 45, 48 }))
    返回 (到文本 ({ 82 }))  ' R

.判断 (DM = 到文本 ({ 48, 48, 48, 48 }))
    返回 (到文本 ({ 83 }))  ' S

.判断 (DM = 到文本 ({ 45 }))
    返回 (到文本 ({ 84 }))  ' T

.判断 (DM = 到文本 ({ 48, 48, 45 }))
    返回 (到文本 ({ 85 }))  ' U

.判断 (DM = 到文本 ({ 48, 48, 48, 45 }))
    返回 (到文本 ({ 86 }))  ' V

.判断 (DM = 到文本 ({ 48, 45, 45 }))
    返回 (到文本 ({ 87 }))  ' W

.判断 (DM = 到文本 ({ 45, 48, 48, 45 }))
    返回 (到文本 ({ 88 }))  ' X

.判断 (DM = 到文本 ({ 45, 48, 45, 45 }))
    返回 (到文本 ({ 89 }))  ' Y

.判断 (DM = 到文本 ({ 45, 45, 48, 48 }))
    返回 (到文本 ({ 90 }))  ' Z

.默认

.判断结束
调试输出 (DM)
返回 (“52+pojie”)


.子程序 PDA


GB = GA

.如果 (GB ≠ “”)

    .判断开始 (GB = “CRAZYNUT”)
        验证.标题 = GC
        GB = “”
        GA = “”
        GC = “”

        返回 ()

    .默认

    .判断结束
    验证.标题 = 到文本 ({ 102, 97, 105, 108 })
    GB = “”
    GA = “”
    GC = “”

.否则
    .判断开始 (ToZM (编辑框1.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
        GA = GA + ToZM (编辑框1.内容)
        GC = GC + 到文本 ({ 115 })
        .判断开始 (ToZM (编辑框2.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
            GA = GA + ToZM (编辑框2.内容)
            GC = GC + 到文本 ({ 117 })
            .判断开始 (ToZM (编辑框3.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
                GA = GA + ToZM (编辑框3.内容)
                GC = GC + 到文本 ({ 99 })
                .判断开始 (ToZM (编辑框4.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
                    GA = GA + ToZM (编辑框4.内容)
                    GC = GC + 到文本 ({ 99 })
                    .判断开始 (ToZM (编辑框5.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
                        GA = GA + ToZM (编辑框5.内容)
                        GC = GC + 到文本 ({ 101 })
                        .判断开始 (ToZM (编辑框6.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
                            GA = GA + ToZM (编辑框6.内容)
                            GC = GC + 到文本 ({ 115 })
                            .判断开始 (ToZM (编辑框7.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
                                GA = GA + ToZM (编辑框7.内容)
                                GC = GC + 到文本 ({ 115 })
                                .判断开始 (ToZM (编辑框8.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
                                    GA = GA + ToZM (编辑框8.内容)
                                    GC = GC + 到文本 ({ 33 })
                                    调试输出 (GA)

                                    调试输出 (GC)
                                    JPDA (真)

                                .默认
                                    JPDA (假)
                                .判断结束



                            .默认
                                JPDA (假)
                            .判断结束


                        .默认
                            JPDA (假)
                        .判断结束



                    .默认
                        JPDA (假)
                    .判断结束


                .默认
                    JPDA (假)
                .判断结束


            .默认
                JPDA (假)
            .判断结束


        .默认
            JPDA (假)
        .判断结束


    .默认
        JPDA (假)

    .判断结束


.如果结束
返回 ()

.子程序 JPDA
.参数 a, 逻辑型

.如果 (a = 假)
    验证.标题 = 到文本 ({ 102, 97, 105, 108 })  ' faild
    GB = “”
    GA = “”
    GC = “”

.否则
    .如果 (“52pojie” = 到文本 ({ 53, 50, 112, 111, 106, 105, 101 }))

        JPDB ()

    .否则

    .如果结束


.如果结束
返回 ()


.子程序 JPDB

.如果 (“ccrraazzyy” = 到文本 ({ 99, 99, 114, 114, 97, 97, 122, 122, 121, 121 }))
    PDA ()

.否则

.如果结束


.子程序 __启动窗口_创建完毕

分析

一共八个输入框,就叫他八位吧,
这里面有一个固定的字符串”CRAZYNUT“ 刚刚好是八位,最终比较的时候还是比较 GB 局部变量是否等于”CRAZYNUT“
而 GB 是 GA 传的值,GA 又是通过解码后算出来的值,一共有八位,会计算八次,计算每一位的时候,如果通过 ToZM 子程序编码规则不能解析为 26 个字母(A,B,C,D,E,F,G,E,F....X,Y,Z)的情况下会直接进入错误处理流程
如果 CA 最终计算的值不等于”CRAZYNUT“ ,进入错误处理流程。

.判断开始 (GB = “CRAZYNUT”)

输入的注册码每一位会和自定义编码规则比较,解码成 26 个字母进行处理,如果不满足解码规则,返回"52+pojie"字串,进入错误处理流程(内容相等返回指定的字符,如果不相等,返回"52+pojie"进入错误处理流程)

{ 53, 50, 43, 112, 111, 106, 105, 101 }=>"52+pojie"
判断条件会成立,进入按钮 fail 处理流程 ,结束验证
.判断开始 (ToZM (编辑框1.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))

例如我们第一位填写的”-0-0“
进入
子程序 ToZM 处理字符

{ 45, 48, 45, 48 }=>”-0-0“ 返回 c
调试输出 (到文本 ({ 45, 48, 45, 48 })) ' c

以下代码判断输入的注册码是否 ={ 45, 48, 45, 48 } =-0-0 如果是返回 C

返回 (到文本 ({ 67 })) ' C```


以此类推计算第二位第三位,在途中任意一位计算不出26个字符,返回"52+pojie",比较后直接进入fail流程,计算完第八位的时候


.判断开始 (ToZM (编辑框 8.内容) ≠ 到文本 ({ 53, 50, 43, 112, 111, 106, 105, 101 }))
GA = GA + ToZM (编辑框 8.内容)
GC = GC + 到文本 ({ 33 })
调试输出 (GA)

调试输出 (GC)
JPDA (真)



JPDA 形参为真 子程序会再次进入
.子程序 PDA
此时的GA赋给GB
判断GB是否等于空,不等于空是否等于 “CRAZYNUT”



.如果 (GB ≠ “”)

.判断开始 (GB = “CRAZYNUT”)





### 总结:
自定义一套26字母编码规则(A,B,C,D,E,F,G,E,F....X,Y,Z),通过.子程序 ToZM 处理和转换

.判断开始 (DM = 到文本 ({ 48, 45 }))
返回 (到文本 ({ 65 })) ' A

.判断 (DM = 到文本 ({ 45, 48, 48, 48 }))
返回 (到文本 ({ 66 })) ' B

.判断 (DM = 到文本 ({ 45, 48, 45, 48 }))
返回 (到文本 ({ 67 })) ' C

.判断 (DM = 到文本 ({ 45, 48, 48 }))
返回 (到文本 ({ 68 })) ' D

.判断 (DM = 到文本 ({ 48 }))
返回 (到文本 ({ 69 })) ' E

.判断 (DM = 到文本 ({ 48, 48, 45, 48 }))
返回 (到文本 ({ 70 })) ' F

.判断 (DM = 到文本 ({ 45, 45, 48 }))
返回 (到文本 ({ 71 })) ' G

.判断 (DM = 到文本 ({ 48, 48, 48, 48 }))
返回 (到文本 ({ 72 })) ' H

.判断 (DM = 到文本 ({ 48, 48 }))
返回 (到文本 ({ 73 })) ' I

.判断 (DM = 到文本 ({ 48, 45, 45, 45 }))
返回 (到文本 ({ 74 })) ' J

.判断 (DM = 到文本 ({ 45, 48, 45 }))
返回 (到文本 ({ 75 })) ' K

.判断 (DM = 到文本 ({ 48, 45, 48, 48 }))
返回 (到文本 ({ 76 })) ' L

.判断 (DM = 到文本 ({ 45, 45 }))
返回 (到文本 ({ 77 })) ' M

.判断 (DM = 到文本 ({ 45, 48 }))
返回 (到文本 ({ 78 })) ' N

.判断 (DM = 到文本 ({ 45, 45, 45 }))
返回 (到文本 ({ 79 })) ' O

.判断 (DM = 到文本 ({ 48, 45, 45, 48 }))
返回 (到文本 ({ 80 })) ' P

.判断 (DM = 到文本 ({ 45, 45, 48, 45 }))
返回 (到文本 ({ 81 })) ' Q

.判断 (DM = 到文本 ({ 48, 45, 48 }))
返回 (到文本 ({ 82 })) ' R

.判断 (DM = 到文本 ({ 48, 48, 48, 48 }))
返回 (到文本 ({ 83 })) ' S

.判断 (DM = 到文本 ({ 45 }))
返回 (到文本 ({ 84 })) ' T

.判断 (DM = 到文本 ({ 48, 48, 45 }))
返回 (到文本 ({ 85 })) ' U

.判断 (DM = 到文本 ({ 48, 48, 48, 45 }))
返回 (到文本 ({ 86 })) ' V

.判断 (DM = 到文本 ({ 48, 45, 45 }))
返回 (到文本 ({ 87 })) ' W

.判断 (DM = 到文本 ({ 45, 48, 48, 45 }))
返回 (到文本 ({ 88 })) ' X

.判断 (DM = 到文本 ({ 45, 48, 45, 45 }))
返回 (到文本 ({ 89 })) ' Y

.判断 (DM = 到文本 ({ 45, 45, 48, 48 }))
返回 (到文本 ({ 90 })) ' Z

.默认

.判断结束
调试输出 (DM)
返回 (“52+pojie”)



在校验输入的时候首先判断是否满足自定义编码后为26个字母,除此以外返回"52+pojie"进入错误处理流程
每一位成功解码后的值再CA局部变量中(不成功解码指的是返回"52+pojie",直接进入错误处理流程),在八位都算出来之后CA 里面存的是这样的字串”ASDSDVSDVSVSD“
八位输入都满足编码后
给子程序JPDA返回一个真,再次进入PDA校验流程,此时CA解码后的值”ASDSDVSDVSVSD“传给GB,GB不再为空 ,进入最终判断,” ”ASDSDVSDVSVSD“ =CRAZYNUT “ 条件为假,进入错误处理流程。

如果解码处理后的CA值为CRAZYNUT,第二次进入PDA   CA赋值给GB,GB里面的值为”CRAZYNUT “与”CRAZYNUT “比较,进入成功处理流程

进入

JPDA返回真进入PDA,
而这个注册码就是自定义了一个编码规则
C转换成"-0-0"
比如说第一位对应C,第二位对应R 这样子,以此类推
C对应一个注册码,R对应一个注册码以此类推...
每一位的内容与注册码比较,相等返回一个C



萌新发帖,觉得楼主码字辛苦了,给点辛苦分吧{:301_991:}。

4 回帖
请输入回帖内容 ...
  • pangluo
  • 其他回帖
  • pangluo
  • huahua

    看上去很高端的样子!!!!我这里有个要解码的,你会帮我吗!

  • pangluo

    私聊🍈