吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8350|回复: 61
收起左侧

[CrackMe] [.NET] 不是人玩的CM系列(一) 奖500CB无壳无混淆,你能破解吗?

  [复制链接]
wwh1004 发表于 2020-8-7 20:40
CM是什么?Crackme是什么?这是什么东西?楼主发的什么?
他们都是一些公开给别人尝试破解的小程序,制作 Crackme 的人可能是程序员,想测试一下自己的软件保护技术,也可能是一位 Cracker,想挑战一下其它 Cracker 的破解实力,也可能是一些正在学习破解的人,自己编一些小程序给自己破解,KeyGenMe是要求别人做出它的 keygen (序号产生器), ReverseMe 要求别人把它的算法做出逆向分析, UnpackMe 是要求别人把它成功脱壳,本版块禁止回复非技术无关水贴。

本帖最后由 wwh1004 于 2020-8-8 11:12 编辑

没有壳没有混淆,我只进行了内联和重命名,不知道这CrackMe可以撑多久。
直接输入你解出的十六进制字符串,类似00112233FFAABBCC这样,长度和下面马赛克差不多(其实答案不唯一,无穷多)。
CM里面有2个公开方法,方便你进行转换,你可以直接构造内部数据转换到十六进制字符串。
第一个做出来的奖励500cb。





没那么简单哦,正确时会显示那一串解密出的字符串(XXX牛逼XXX),没解密出不算。就像软件显示已注册但是实际功能缺失叫伪破解

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 14吾爱币 +15 热心值 +11 收起 理由
guoxinjie17 + 1 + 1 我很赞同!
LanYun + 1 热心回复!
陈世界 + 1 + 1 我很赞同!
xiaopeng_faith + 1 谢谢@Thanks!
低调丶骇小客 + 1 厉害厉害!
JokerLin + 1 + 1 热心回复!
wdy2670272937 + 1 鼓励转贴优秀软件安全工具和文档!
lymi_2771 + 1 + 1 热心回复!
dgy + 1 热心回复!
CrazyNut + 6 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
那棵大树疯了 + 1 + 1 用心讨论,共获提升!
慕若曦 + 1 + 1 wwh1004师傅太强了
明月心 + 1 都别下载了。这不是人玩的。下载的都不是人 = =
小十二 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

JemmyloveJenny 发表于 2020-8-8 23:27
本帖最后由 JemmyloveJenny 于 2020-8-8 23:41 编辑

这个CM好复杂啊,差不多用了6个小时才做出来……

这个程序就相当于一个计算器,先举个栗子讲:
随便写一个运算式 1+2,然后我们把它改写成先运算符号,再两个操作数的形式
比如 1+2改写成{+;1;2},+,1,2分别叫做运算类型,操作数1,操作数2,
表达式也可以成为操作数,比如说((2+3)*(x^4))/e^x先展开成{/;(2+3)*(x^4);e^x}
然而数学算式是有运算顺序的,不能先算外部再算内部,因此需要把整个算式展开成数字的运算
先算value1={+;2;3},再算value2={^;x;4},然后计算value1={*;value1;value2}value2={^;e;x},最后计算result={/;value1;value2}

我们看一下GClass1的结构

public class GClass1
{
        public byte byte_0;/* 运算类型,具体运算见smethod_6 */
        public object object_0;/* 操作数1,可以是数字,也可以是另一个GClass1(表达式) */
        public object object_1;/* 操作数2,可以是数字,也可以是另一个GClass1(表达式) */
}

这个CM的主要内容,便是用GClass的嵌套实现进行运算的目的
smethod_6是计算器的具体实现,我化简了一下标了一些备注贴在最后,看一下大概理解就行

接下来就可以开始分析Main主程序了
首先是让我们输入一串字符,这个字符串会被smethod_1转换成为一个GClass1,也就是一个数学算式
我们把输入的数学算式记作f(x)

往下看到调用了smethod_6,具体如下

                object obj5 = (gclass2 != null) ? GClass0.smethod_6(gclass2, new object[]
                {
                        num /* 此时num=0.0 */
                }) : obj4

传入的num是自变量x,这就相当于obj5=f(0)
接下来是一串类型转换,直接跳过不看,可以把那段类型转换记作Object2Double

后面再次出现了熟悉的smethod_6Object2Double,不过这次参与运算的表达式不是f(x),而是null(因为尝试把int 1转换为GClass1,转换失败得到了null)
这次smethod_6运算的结果是1

然后有一个判断语句

                /* num8=1, num7=f(0) */
                if (Math.Abs((num8 - num7) / num7) >= 1E-05)
                {
                        gclass4 = null;
                }
                else
                {
                        ······
                }

如果if成立,那么就会执行gclass4=null,会导致后面出现NullPointerException,所以if不可以成立
这个语句看起来是在要求|(1-f(0))/f(0)|<0.00001,但是后来想想1E-05貌似也没什么意义,应该是判断等于0的意思
所以利用数学知识可以解得一个条件f(0)=1

构造一个符合f(0)=1的表达式之后,就可以进入else分支
看到程序内构造了一个GClass1

                        GClass0.GClass1 gclass5 = new GClass0.GClass1
                        {
                                byte_0 = 22,
                                object_0 = gclass,/* gclass就是输入的f(x) */
                                object_1 = 0/* 必须为0 */
                        };

这个byte_0=22是求导的意思,具体展开需要研究smethod_6(贴在最后了),我标了一些注释,应该能看懂
所以得到了此处的表达式d/dx(f(x))

接下来又出现了一串字符串,这个字符串会被处理成GClass(就和我们输入的内容一样)
注意,这里把gclass1中的object_1(操作数1)替换成了f(x)
根据 运算类型,操作数1,操作数2 解读GClass便可以得到第二个表达式 e^x*(2x+3)+f(x)

之后就进入了一个for循环,x的取值遍历了0-100之间每个0.1,共1000个值
d/dx(f(x))e^x*(2x+3)+f(x)会被赋予自变量x的值,并且求出这两个表达式的具体结果
对这两个表达式的具体结果进行判断

                                /* num11 = `e^x*(2x+3)+f(x)` */
                                /* num7 = `d/dx(f(x))` */
                                if (Math.Abs((num11 - num7) / num7) >= 1E-05)
                                {
                                        gclass4 = null;
                                        goto IL_3CC;
                                }

这里的if同样不可以成立,并且由于if处于for循环内部,也就是说对于任意满足0<x<100的x都要有 ((e^x*(2x+3)+f(x))-(d/dx(f(x))))/(d/dx(f(x))) < 0.00001 成立
这个1E-05也是等于0的意思,因此条件可以化简为 e^x*(2x+3)+f(x) = d/dx(f(x))

然后…这个东西我就不会做了(高中并没有教导数233333)
小猿搜题拍一下,可以知道f(x)=(x^2+3x+1)*e^x
所以我们只要构造出这个表达式的GClass,然后调用CM里的smethod_3转化为字符串就行了
演示一下:

                        new GClass1() {
                                byte_0 = 18,/* 乘法 {*;x^2+3x+1;e^x} */
                                object_0 = new GClass1() {
                                        byte_0=16,/* 加法 {+;x^2;3x+1} */
                                        object_0= new GClass1()
                                        {
                                                byte_0 = 21,/* 乘方 {^;x;2} */
                                                object_0 = new GClass1()
                                                {
                                                        byte_0 = 7,/* 取值 {获取x的值} */
                                                        object_0 = 0,
                                                        object_1 = null
                                                },
                                                object_1 = 2
                                        },
                                        object_1=new GClass1() {
                                                byte_0=16,/* 加法 {+;1;3x} */
                                                object_0=1,
                                                object_1=new GClass1() {
                                                        byte_0=18,/* 乘法 {*;3;x} */
                                                        object_0=3,
                                                        object_1=new GClass1()
                                                        {
                                                                byte_0 = 7,/* 取值 {获取x的值} */
                                                                object_0 = 0
                                                        }
                                                }
                                        }
                                },
                                object_1= new GClass1()
                                {
                                        byte_0 = 20,/* e乘方 {e^;x} */
                                        object_0 = new GClass1()
                                        {
                                                byte_0 = 7,/* 取值 {获取x的值} */
                                                object_0 = 0,
                                                object_1 = null
                                        }
                                }
                        }

这个GClass转换成字符串的结果就是
1204100415040700000000000002000000041000010000000412000300000004070000000000041404070000000000
输入到程序里面验证一下,正确!收工了!

最后贴上简化注释过的smethod_6:

private static object smethod_6(GClass0.GClass1 gclass1_0, params object[] object_0)
{
        Stack<Tuple<GClass0.GClass1, object[], object, object, bool, int>> stack = new Stack<Tuple<GClass0.GClass1, object[], object, object, bool, int>>();
        stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, null, null, false, 0));
        object obj = null;
        for (;;)
        {
                Tuple<GClass0.GClass1, object[], object, object, bool, int> tuple = stack.Pop();
                gclass1_0 = tuple.Item1;
                object_0 = tuple.Item2;
                object obj2 = tuple.Item3;
                object obj3 = tuple.Item4;
                bool flag = tuple.Item5;
                int item = tuple.Item6;
                switch (item)
                {
                case 0:
                        /* item=0时,GClass1中的操作数分别放到obj2,obj3,前一步骤的运算结果obj不处理 */
                        obj2 = gclass1_0.object_0;
                        obj3 = gclass1_0.object_1;
                        flag = (obj2 is GClass0.GClass1);
                        /* 判断第一个操作数obj2是数字,还是算式 */
                        if (flag)
                        {
                                /* 如果obj2是算式,先Push自身item=1(具体含义见case1),并把obj2的表达式Push到stack上 */
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 1));
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj2, object_0, obj2, obj3, flag, 0));
                                goto IL_A66;/* 直接开始循环 */
                        }
                        flag = (obj3 is GClass0.GClass1);
                        /* 第一个操作数不是算式,接下来判断第二个操作数obj3是数字,还是算式 */
                        if (flag)
                        {
                                /* 如果obj3是算式,先Push自身item=2(具体含义见case2),并把obj3的表达式Push到stack上 */
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 2));
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj3, object_0, obj2, obj3, flag, 0));
                                goto IL_A66;
                        }
                        goto IL_1E;/* 直接开始循环 */
                case 1:
                        if (flag)
                        {
                                obj2 = obj;/* 如果之前运算的东西是个算式,把结果从obj放入obj2(因为item=1) */
                        }
                        flag = (obj3 is GClass0.GClass1);
                        if (flag)
                        {
                                /* 同上,把obj3表达式Push到stack上 */
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 2));
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj3, object_0, obj2, obj3, flag, 0));
                                goto IL_A66;
                        }
                        goto IL_1E;
                case 2:
                        if (flag)
                        {
                                obj3 = obj;/* 如果之前运算的东西是个算式,把结果从obj放入obj3(因为item=2) */
                        }
                        goto IL_1E;
                case 3:
                        goto IL_1E;/* 不改变obj2,obj3,还保留了上一步的结果obj */
                }
                goto Block_101;
                IL_A66:
                if (stack.Count == 0)/* 如果stack上的东西都算完了 */
                {
                        return obj;/* 返回运算结果 */
                }
                continue;
                IL_1E:
                switch (gclass1_0.byte_0)/* switch 运算类型 */
                {
                /* 类型判断被我精简过了,含义应该自己就能看懂 */
                /* 一元运算 */
                case 0:
                {
                        obj = Object2Int(obj2);/* 把操作数1(Tuple.Item3)转为Int放到结果(obj)内 */
                        goto IL_A66;
                }
                case 1:
                {
                        obj = Object2Long(obj2);
                        goto IL_A66;
                }
                case 2:
                {
                        obj = Object2Float(obj2);
                        goto IL_A66;
                }
                case 3:
                {
                        obj = Object2Double(obj2);
                        goto IL_A66;
                }
                /* 有关传入自变量(x)的运算(主要就是取出自变量的值) */
                case 4:
                {
                        obj = (int)object_0[Object2Int(obj2)];/* 理论上,obj2可以使任何整数,但是由于传入的object_0都是只含有一个元素,所以操作数1(obj2)必须为0 */
                        goto IL_A66;
                }
                case 5:
                {
                        obj = (long)object_0[Object2Int(obj2)];
                        goto IL_A66;
                }
                case 6:
                {
                        obj = (float)object_0[Object2Int(obj2)];
                        goto IL_A66;
                }
                case 7:
                {
                        obj = (double)object_0[Object2Int(obj2)];
                        goto IL_A66;
                }
                /* 接下来是二元的加减乘除 */
                case 16:
                {
                        obj = Object2Double(obj2) + Object2Double(obj3);
                        goto IL_A66;
                }
                case 17:
                {
                        obj = Object2Double(obj2) - Object2Double(obj3);
                        goto IL_A66;
                }
                case 18:
                {
                        obj = Object2Double(obj2) * Object2Double(obj3);
                        goto IL_A66;
                }
                case 19:
                {
                        obj = Object2Double(obj2) / Object2Double(obj3);
                        goto IL_A66;
                }
                /* 乘方类运算 */
                case 20:
                {
                        /* 计算e^obj2,e为自然底数 */
                        obj = Math.Exp(Object2Double(obj2));
                        goto IL_A66;
                }
                case 21:
                {
                        /* 计算乘方 obj2^obj3 */
                        obj = Math.Pow(Object2Double(obj2), Object2Double(obj3));
                        goto IL_A66;
                }
                /* case22相当于计算了(f(x+10^-8)-f(x))/(10^-8),也就是计算f(x)的导数 */
                case 22:/* 对于case22来说,操作数1必须是算式,Main里传递过来的是算式f(x) */
                {
                        object obj7;
                        if (item != 3)/* 判断是否展开过求导运算 */
                        {
                                /* item!=3表明没有展开过,开始展开运算 */
                                object[] object_1 = Copy(object_0); /* Copy是伪代码,复制一下传入参数数组object_0,复制到object_1 */
                                obj7 = object_0[Object2Int(obj3)]; /* 把传入参数中的第(obj3)个取出(obj3只能为0) */
                                object_1[Object2Int(obj3)] = Object2Double(obj7) + 1E-08;/*  把取出的值加上1E-08, 即0.00000001,一个足够小的值*/
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 3));/* 此时压入f(x)表达式,item=3指示已经展开过求导运算 */
                                stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)gclass1_0.object_0, object_1, obj2, obj3, flag, 0));
                                /* 把f(x)展开后压入,由于object_1中的自变量被加上了1E-08,因此,实际运算的是f(x+1E-08) */
                                goto IL_A66;
                        }
                        obj = (Object2Double(obj) - Object2Double(obj2)) / 1E-08;/* obj是后Push的f(x+1E-08)的值;obj2是先Push的f(x)展开式的值 */
                        /* 最终求得导数f'(x) */
                        goto IL_A66;
                }
                }
                break;
        }
}

点评

厉害,还是单独来一篇独立教程吧,方便大家学习加分。  详情 回复 发表于 2020-8-8 23:45

免费评分

参与人数 18吾爱币 +25 热心值 +17 收起 理由
Zxis + 1 + 1 谢谢@Thanks!
Light紫星 + 2 + 1 用心讨论,共获提升!
黑猫的猫 + 1 + 1 np
情书三行 + 1 我很赞同!
Mr_Guan + 1 + 1 我很赞同!
圣甲炫 + 1 + 1 我很赞同!
zhan + 1 + 1 我很赞同!
pojie_huahua + 1 + 1 我很赞同!
青春已不在丶 + 1 + 1 我很赞同!
lostlq + 1 + 1 我很赞同!
hszt + 1 + 1 天啦,这也太牛了吧
LC学破解 + 2 + 1 我很赞同!
魔道书生 + 2 + 1 我很赞同!
qaz003 + 2 + 1 佩服。。这么好耐性。。。
不爱everyone + 1 + 1 厉害厉害
flow_one + 1 + 1 我很赞同!
cdj68765 + 2 + 1 我很赞同!
yAYa + 3 + 1 所以说我52论坛真的是藏龙卧虎!师傅厉害了

查看全部评分

WoShiXXX 发表于 2020-8-8 08:31
既然是CM,那为什么不能爆破呢?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 2吾爱币 +2 收起 理由
nue12138 + 1 热心回复!开头都说了,你这叫伪爆破
魂殇 + 1 我很赞同!

查看全部评分

梦游枪手 发表于 2020-8-9 01:40
本帖最后由 梦游枪手 于 2020-8-9 01:53 编辑

楼上的大佬给出答案了,我就发个求原函数的过程吧

f'(x) = e^x * (x * 2 + 3) + f(x)

f'(x) - f(x) = e^x * (x * 2 + 3)

方程两边乘以 e^-x

e^-x * (f'(x) - f(x)) = 2*x + 3

因为 [e^-x * f(x)]' = e^-x * (f'(x) - f(x))

所以 [e^-x * f(x)]' = 2*x + 3

两边求积分

e^-x * f(x) = x^2 + 3*x



f(x) = (x^2 + 3*x + 1) * e^x



根据解出的f(x)拼凑对应的字符即可,拼凑格式为
运算+类型+num0+num1(两个num均为小端序,如果类型为4说明要嵌套,也可以当成括号)


运算类型(数字为10进制,拼凑时自行转16进制)

0-3 类型转换(int long float double)

4-7 读入参数(也就是x)并强转类型 类型顺序同上(实际上能用的只有7,其他的用了会抛异常)

16-19 num0 num1 加减乘除

20 e^(num0) 也就是常数 e 的 num0 次方

21 num0^num1

22 求导

num的类型则有5种,0->int  1->long,2->float,3->double,4->嵌套运算(括号)
为了方便拼凑,常数部分可以选择取int,当然如果愿意都换成double应该也可以。

多解也是有的,可以把括号去掉啊,或者调换括号内的运算顺序等等,自行发挥想象力,这里给出跟楼上大佬不一样的一组(虽然也很像就是了)
1204100410041504070000000000000200000004120407000000000000030000000001000000041404070000000000


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册[Register]

x

免费评分

参与人数 4吾爱币 +6 热心值 +4 收起 理由
某科学的阿卡林 + 1 + 1 枪手dalao啥都会
Hmily + 1 + 1 用心讨论,共获提升!
wwh1004 + 3 + 1 我很赞同!
大白痴先生 + 1 + 1 大哥你好啊,你能不能也写个分析过程啊,谢谢啦!

查看全部评分

 楼主| wwh1004 发表于 2020-8-7 20:53
其实就是个数学原题,如果你看得懂我的代码,甚至可以百度到答案
慕若曦 发表于 2020-8-7 22:21
下载了瞄了一眼直接放弃……类型转来转去头痛,不是人玩的
92013 发表于 2020-8-7 22:21
是不是数学渣的就不用看了~
solly 发表于 2020-8-7 23:39
.Net Reflactor v10.1 看不了 smethod_1()。
 楼主| wwh1004 发表于 2020-8-7 23:42
solly 发表于 2020-8-7 23:39
.Net Reflactor v10.1 看不了 smethod_1()。

ilspy可以看,这些都没混淆,我只是内联了一些方法,所以看起来代码很多,比如一堆类型判断
solly 发表于 2020-8-7 23:46
本帖最后由 solly 于 2020-8-7 23:47 编辑

wwh1004 发表于 2020-8-7 23:42
ilspy可以看,这些都没混淆,我只是内联了一些方法,所以看起来代码很多,比如一堆类型判断

.NET 这块我并不熟悉,觉得这些类型转换应该是没有指定类型或auto类型、泛型之类的操作吧。

好象后面输出的汉字是aes解密出来的。
JemmyloveJenny 发表于 2020-8-8 00:29
好像看出数列题目内味了…?
好想明天社工一下你的同学,找到数学原题呐
006306 发表于 2020-8-8 05:35
好家伙,指针数组,开幕雷击
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-26 13:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表