记一个DLL授权的简单暴破

查看 89|回复 7
作者:lang1yd   
一,需求:
将统计表中 应收余额改为应收金额,并隐藏现金收入,储值扣款,过车数量


01原始界面.png (124.15 KB, 下载次数: 0)
下载附件
1
2024-10-30 13:05 上传

二,分析:
(一)Exeinfo查出,程序为.net写的:
用dnspy打开,发现有些变量名是乱码的,导出工程后,在vs中打开,发现会有一些资源丢失了, 用ILSpy打开,再导入到VS中,好多了.系统提示只有为数不多的几个语法错误,一一修复.然后就可以生成exe了.
(二)初步运行
将生成的exe放到原目录,可以打开登录界面并能成功连接数据库,登录系统.但是会弹一个缺少资源的错误

在ILSpy和dnspy中都有看到有这个资源,但是无法导出有效内容,
// Park_manage.Properties.Resources.resources (Embedded, Public)
于是在工程中随意增加一个resources,就不报这个错了,但再次打开时会弹出一个未授权的窗体,



05程序未授权.jpg (10.09 KB, 下载次数: 0)
下载附件
3
2024-10-30 13:05 上传

确定后,就会强制关闭程序,并会打开一个网页,指示未授权.
但是这个原始主程序是可以正常使用的,已经添加了授权的,所有功能都可以正常使用,即始没有授权,也会有一个试用期,不确定这个新打包的程序弹出的界面是从哪冒出来的.
(三)分析主程序
根据网页的提示,搜索到这个是一个需要授权的插件,但是这个原程序中已经就有这个授权了,猜测解包时,授权签名不能导出,可能就是前面提到的Properties.Resources.resources资源文件, 再打包后,就是授权签名就不存在,所以就会提示程序未授权了.
在程序目录中的确有一个前面网页中提示的dll文件.
在主程序中搜索一番,发现有大量调用这个插件里的库, 但一时也不知道这个窗口是从哪个位置调用出来的,
也搜索不到这个字串,搜索MessageBox函数,也没有找到有用的信息.于是在主程序中不断的下断点,结果虽然知道是在 Form_Login 之后的Form_Main中触发的,但是始终找不到具体的位置,用MessageBox来做断点时,初步确定是在某个控件时,dll中自己弹出的.
(四)分析Dll
查看dll,发现也是c#的,用ILSpy查看时,发现是加了混淆的,



06混淆dll.jpg (92.97 KB, 下载次数: 0)
下载附件
4
2024-10-30 13:05 上传

尝试用 de4dot,处理下,结果相当哇噻.



07解混淆.jpg (75.98 KB, 下载次数: 0)
下载附件
5
2024-10-30 13:05 上传

于是载入vs中分析,开始依然是处理语法问题,其中有2处方法,显示0引用,方法体很简单,就直接注释了,后面有问题再说.
[C#] 纯文本查看 复制代码                 /*
[CompilerGenerated]
private bool method_0(_003C_003Ef__AnonymousType0 _003C_003Ef__AnonymousType0_0)
{
   return _003C_003Ef__AnonymousType0_0.a == this;
}*/
语法处理完毕,再生成dll成功,先将 exe和dll都放在原安装目录,dll替换掉原来的,再运行exe,会提示
引发的异常:“System.IO.FileNotFoundException”(位于 mscorlib.dll 中)
**string_0** 是 null。


08为string0为null.jpg (137.34 KB, 下载次数: 0)
下载附件
6
2024-10-30 13:05 上传


发现 这个source就是刚好就是处理的这个dll, 将 string_0 == ""  改为 string_0 == null || string_0 == "" 解决下参数错误的问题 ,同时发现这个模块()中,有很多加密解密相关的方法,记下模块名为 internal static class Class4
点继续,弹出弹出未授权的窗口.
到这里,exe和dll在功能上基本恢复了.
(五)正式解密
(1) 从出错的地方分析:
查看调用来自一个简类:
[C#] 纯文本查看 复制代码internal class Class5
{
        private static string string_0;
        internal static string smethod_0(string string_1)
        {
                return Class4.smethod_1(string_1, smethod_1("8Mv这一串码部分已作隐藏处理MtNcN"));
        }
        private static string smethod_1(string string_1)
        {
                try
                {
                        int num = 0;
                        int length = string_0.Length;
                        byte[] array = new byte[string_1.Length / 2];
                        for (int i = 0; i
用测试demo 测试这个解码1函数:  
Console.WriteLine(Class5.smethod_1("8Mv这一串码部分已作隐藏处理MtNcN "));  
返回结果
这一串码部分已作隐藏处理papa
就是说  Class5.smethod_0(string string_1) 执行的是一个解码2函数 ,相当于
string Class4.smethod_1(string_1, “这一串码部分已作隐藏处理papa”);
这个结果先放到一边,另一边,在dll中搜索MessageBox.Show,结果很有用,

除开 MessageBox.Show(ex.Message);的try错误后,有几个有用的:
MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));
MessageBox.Show(string_0);
先看 MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));所在的函数在 internal class Class6中的:
[C#] 纯文本查看 复制代码        private static void smethod_4()        {
                bool_0 = false;
                int num = 0;
                while (true)
                {
                        num++;
                        if (Application.OpenForms.Count = 3)
                                {
                                        break;
                                }
                                Thread.Sleep(1000);
                                continue;
                        }
                        Application.OpenForms[0].Invoke((MethodInvoker)delegate
                        {
                                MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));
                                Process.Start(Class5.smethod_0(Resources.smethod_37()));
                                Environment.Exit(0);
                        });
                        return;
                }
                Process.Start(Class5.smethod_0(Resources.smethod_37()));
                Environment.Exit(0);
        }
参数Resources.smethod_36()调用为
[C#] 纯文本查看 复制代码                 internal static string smethod_36()
                {
                        return ResourceManager_0.GetString("t", cultureInfo_0);
                }
                internal static ResourceManager ResourceManager_0
                {
                        get
                        {
                                if (resourceManager_0 == null)
                                {
                                        ResourceManager resourceManager = resourceManager_0 = new ResourceManager("xxxx.Properties.Resources", typeof(Resources).Assembly);
                                }
                                return resourceManager_0;
                        }
                }
依次追踪,发现最终是读取资源文件中的t字符串,资源名称为  xxxx.Properties.Resources,找到里面的资源文件,发现有t值:
t值为: 5E2B7这一串码部分已作隐藏处理37226636EF68
将t值取出,做了一个demo代入到
MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));
得到 “程序未授权!(Unauthorized program!)” 这个字符串,
我说开始搜索不到这个串呢,原来也是要解码的.
得到结论,  Class6.smethod_4() 就是弹出个需要授权窗口的操作,再看看另外一个 MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));,也在class6里面的这个方法内:
[C#] 纯文本查看 复制代码        private static void smethod_14()
        {
                string text = Class4.smethod_1(smethod_20(typeof(Class6).Assembly.Location), Resources.mm.smethod_2()).smethod_3();
                if (!string.IsNullOrEmpty(text))
                {
                        string[] array = text.Split('^');
                        if (array.Length == 2)
                        {
                                string text2 = array[0];
                                int num = Convert.ToInt32(array[1]);
                                if (!string.IsNullOrEmpty(text2) && num != 0)
                                {
                                        bool flag = false;
                                        switch (num)
                                        {
                                        case 1:
                                        {
                                                string value = class3_0.method_2();
                                                flag = text2.Equals(value);
                                                break;
                                        }
                                        case 2:
                                        {
                                                List list = smethod_21();
                                                list.AddRange(smethod_22());
                                                flag = list.Contains(text2);
                                                break;
                                        }
                                        }
                                        if ((num == 1 || num == 2) && flag)
                                        {
                                                if (num == 2)
                                                {
                                                        smethod_15(text2);
                                                }
                                                if (bool_1 && thread_1 == null)
                                                {
                                                        thread_1 = new Thread(smethod_3);
                                                        thread_1.IsBackground = true;
                                                        thread_1.Start(text);
                                                }
                                                bool_0 = true;
                                                return;
                                        }
                                }
                        }
                }
                bool_0 = false;
                MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));
                Process.Start(Class5.smethod_0(Resources.smethod_37()));
                Environment.Exit(0);
        }
这里面最后的操作:
                bool_0 = false;
                MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));
                Process.Start(Class5.smethod_0(Resources.smethod_37()));
                Environment.Exit(0);
在两个调用中都出现了,现在分析下具体功能:
bool_0 = false;  为class6的一个属性,猜测为是否已授权
MessageBox.Show(Class5.smethod_0(Resources.smethod_36()));  弹出未授权
Process.Start(Class5.smethod_0(Resources.smethod_37()));    用demo解码出来后 指示一个网页  http://d.xxxx.net/noauthorize.aspx  ,就是跳到这个未授权的网页
Environment.Exit(0);   当然就是退出程序了.
(2)
到这里,就有一个简单的暴破的方案:

就是在 Class6.smethod_4() 中直接返回,和 Class6.smethod_14() 中设置bool_0 = true,再直接返回,就行了.
(3)
再次运行,提示缺少资源,MenuTitle.Image = (Image)resources.GetObject("MenuTitle.Image"); 一个图片,直接就是甩一个图片进去.完成后,就可以正常进入系统了.不再弹出授权窗口.
三,后续处理:
(一)
返回exe中,找到相关界面:
namespace Park_manage.statistice  中的  public class Form_monthSumList.cs 和 public class Form_monthSumLis.resx,
修改界面,SQL的操作修改,这里与授权无关,就不再赘叙了.完成后的图



11完成后,cpu高.jpg (191.74 KB, 下载次数: 0)
下载附件
8
2024-10-30 13:05 上传

(二)
有个问题,这个cpu占用一直很高,想到dll中原来会有一个  Thread.Sleep(1000);
于是在这个方法中返回之前 添加一个延时10秒,再次运行,瞬间安静下来了.
        private static void smethod_4()
        {
                Thread.Sleep(10000);
                return;



12修复CPU.jpg (178.17 KB, 下载次数: 0)
下载附件
9
2024-10-30 13:05 上传

至此,需求问题就算是初步解决了.这里不想做进一步的授权侵入操作,点到为止.刹果.
四,结束:
文章只是提供了一个解决思路,程序理清后相对简单,以前没有遇到在dll中嵌入授权的情况,特此分享经验.
请勿用于任何非法用途,文章内不提供任何成品代码,思路仅供学习。转载请注明出处

下载次数, 弹出

cwoy   

学习思路
Tyris   

嗯,很不错的指导
tonyhaha   

不错,学习
lizhenqiang1990   

有点意思,做二次混淆确实比较麻烦;
az730308539   

日常来,学习学习,很详细
uLY3M   

学习,不错的文章
yaoguen   

谢谢分享,学习了
您需要登录后才可以回帖 登录 | 立即注册

返回顶部