吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 69947|回复: 113
收起左侧

[.NET逆向] .NET破解之XX地图下载器【非暴破】

    [复制链接]
我也是个傻瓜 发表于 2015-8-29 23:54
本帖最后由 云在天 于 2019-6-7 13:13 编辑

不知不觉,接触破解逆向已经三个月了,从当初的门外汉到现在的小白,这个过程只有经历过才知道其中的苦与乐:
有无知、困惑、痛苦、惊喜、彻悟、欣慰……
有无助的软件脱壳,茫然的代码分析,有无趣的反复测试,
有人说破解不应该程序员干的事,我回复我不是程序员;
有人说.NET程序太没难度了,去破安卓,后来我真开始研究起安卓来;
有人说能不能把支付宝破了,我没回答。
虽然很忙,时间少,但每天晚上都要抽些时间来关注52论坛,关注破解;虽然并没有从破解中获取物质利益,但我愿意将自己的心得或作品分享给大家。不管怎样,它将作为我的一门兴趣爱好继续发展下去。
本文为详细分析,新手们也可以尝试着做一下。
一、对象描述
作为GIS及相关专业,想必都接触过地图下载吧,市面上地图、影像下载工具都非常多,效果好的、差的,免费的、收费的……各有优劣。我曾经分析过一篇文章《那些年,我们用过的地图下载器》,需要的朋友可以去看看,今天我们的目标就是一个地图下载器——太乐地图下载器。
其官方网址是:http://www.arctiler.com/ ,最新版本是:太乐地图下载器 V4.9
还是那句老话:严格意义上来说,地图下载器已经触犯了地图服务商的权益(只不过别人没有来找你),有关方面不要给我留言或发邮件,如果这样,相当于干着侵权的事的人来指责其他侵犯自己的权益。
《计算机保护条例》中说了,单方面的免费声明是无效的,我还是要像地图下载器开发者一样做一个免责声明:本教程仅供研究学习,请在24小时之内删除相关软件、忘记相关内容
二、环境说明
操作系统:Win7 64位原版(由于笔记本配置太差,虚拟机都没装)
使用工具:de4dot 3.1(脱壳).NET Reflector8.5(主要分析工具)、Reflexil1.9(修改,它是.NET Reflector插件)、ILSpy2.1(调试)、VS(辅助分析、写注册机)。这些工具网上都能找到,这里就不用提供了吧。
三、详细流程
还是以前的原则,破解程序的大致流程是:试用脱壳-寻找关键-分析修改-调试发布。
01.安装试用
去官网下载安装,在帮助文档的说明中,分别说明了共了免费版、标准版、专业版和企业版,其功能与价格成正比。
试用,它是试用版,有功能限制,比如下载的等级、数据量大小等,这是一般的商业软件模式。在试用过程中,发现其限制的地方,为后面的寻找关键作好准备。
02.软件脱壳
脱壳是实际上一个有难度的活,由于我现在还是工具党,还用的脱.NET壳神器de4dot,(大家还有没有好的脱壳神器或手动脱壳的教程,分享一下呗)它的成功率应该有60%-70%左右。如果把它放在.NET Reflector8.5中出现"索引超出了数组界限"
它的脱壳结果有三种:一是脱壳后代码没有了混淆,程序可以运行,这是最好的结果;二是脱壳后代码还是有部分混淆,程序可以运行,这个结果也勉强能接受;三是脱壳后代码没有混淆,程序不可以运行,这也是最痛苦的(这多半要手动来脱壳)。
本程序遇到了两个关键文件AZMap.exe属于第一种, AZMap.Core.dll属于第三种,也就是说我不能对AZMap.Core.dl修改,只能修改AZMap.exe。
03.寻找关键
将脱壳后的AZMap.exe加到.NET Reflector,使用go to entry point和search是两种常用的入手方法,前者可以到达程序的main函数,然后可一步一步往下分析,后者找出与限制相关的地方,然后可一 一分析。
Main函数中,没有什么异样,直接进入即可。想一下,下一步,程序主界面出现后就知道你是试用版还是其他版本,这说明在主界面出现的过程中必定有函数或方面来检测版本问题,而MainForm里只有两个函数存在这样的效果:构造函数和Load函数(其他程序破解也有类似规律)
果然,MainForm的构造函数中调用了InitLicense方法,赶快标记起来,可能有用。
分析InitLicense方法中的代码,其中RegisterProduct枚举有Control,Desktop,Server三个。
[C#] 纯文本查看 复制代码
//根据经验,这个字符串是经过加密函数加密后的
//最后有个=号,你可能见过这样的,猜测这是“Desktop”的含义
SN.RegisterProductCode = "wfg783X8Joo=";
//检查权限类型,这个整个分析的关键
LicenseType lic = SN.Registered(1);
//刷新窗体显示的权限版本
this.RefreshFormTitle(lic);
//启动加密狗监听
this.StartDogListener();
  OK,我们显然要进入SN.Registered看看它具体是什么。等一下,想一下,以我以前的脾气,我会直接让这个方法返回想要的类型值,直接暴破,但这里SN类是AZMap.Core.dll中,我们不能修改,所以本程序破解的方式是分析它的算法,SN类的Registered是我们要分析的关键。(这句话说起来简单,这也是我尝试过好几次得到的结论和方面,平时处理过程中,新手应该不会这么快就得出这样的结果,可能也找了很久才找到关键,而且还不一定是真正的关键,所以要有耐心!)
04.代码分析
新手入门都是暴破,这样来得快,我这次也是迫不得已(不会脱壳,高手勿喷)才来分析它的算法的。下面这个过程可能有点让人痛苦:
SN.Registered函数是读取许可文件的,其关键还在于调用的另外一个函数Register
[C#] 纯文本查看 复制代码
//返回的LicenseType(枚举值,有Enterprise, Professional, Standerd, Trial, Free, Given),我们想要的当然是枚举值=0的企业版。
//输入参数是lic许可文件路径和要注册的类型(1代表Desktop,这个我们可以不管,在这个程序注册的都是它)
public static LicenseType Register(string licPath, RegisterProduct regType = 1)
{
    LicenseType type2;
    //初始化为Free版,我就是就想让free的值赋成Enterprise
    LicenseType free = LicenseType.Free;
    if (!File.Exists(licPath)) return free;
    //读取许可文件中的许可码
    string sn = ReadSN(licPath);
    //如果是Desktop,许可文件路径为C:\Users\Administrator\AppData\Local\AZMap
    string path = GetLocalAZMapPath(RegisterProduct.Desktop) + "azmap_4";
    // 如果是Server,许可文件路径为X:\Program Files (x86)\ArcTiler\Desktop\4.9\AZMap
    string str3 = GetLocalAZMapPath(RegisterProduct.Server) + "azmap_4";
    try
    {
        string r = "";
        bool flag = false;
        //规律一:SN长度为175-250个
        // IsSNLegality函数检查SN是否非法,其具体要求是SN长度为175-250个
        if (IsSNLegality(sn))
        {
            //规律二:SN的72位到104位与机器码加密后的字符串必须相等
            //我们想要flag的值返回为ture,就需要看SetSN函数返回值。获取机器码加密后的值是否与r相等。根据它提供的计算函数,我在VS把它生成出来了
            //如果SN从72位开始截取32位(即r值)与MCToR()函数返回值(获取机器码加密后的值)相当,则flag为真。
            r = sn.Substring(72, 32);
            flag = R.Instance.SetSN(r);
        }
        if (!flag) goto Label_02B4;
                
        //规律三:SN中至少有两个#号
        //将SN用#进行分割,从下文看,使用了strArray[1],strArray[2],说明至少有两个#号
        string[]strArray = sn.Split(new char[] { '#' });
        int result = 0;
        //规律四:#号分割后的SN,strArray[1]为版本类型代码加密后的值
        // 用Decode函数将strArray[1]解密后转为int类型,out给result(是LicenseType的代码),我们的目的是想将Enterprise(当Version =0,1,2时,其代码-1,-11,-21),根据调试,本程序当Version =0,所以,我们是想把-1out给result
        //如果Decode解密后的值不是数字,则TryParse转换失败,将返回为False值,那就完蛋了。
        if (!int.TryParse(EncAndDec.Decode(strArray[1], EncAndDec.ToMD5("KEY_64"), EncAndDec.ToMD5("IV_64")), out result))
        {
            free = LicenseType.Trial;
            return LicenseType.Trial;
        }
        //经测试,其他Version为0,所以,Version=1,2的代码忽略掉了
        if (ApplicationConfig.Version == 0)
        {
            //在VS测试,能够得到 result=-1,达到了free的值赋成Enterprise的目的
            switch (result)
            {
                case -1:
                    free = LicenseType.Enterprise;
                    // 转换到Label_0170,这应该是脱壳没干净的原因,不然不会有跳转
                    goto Label_0170;
                case -3:
                    free = LicenseType.Professional;
                    goto Label_0170;
                case -5:
                    free = LicenseType.Standerd;
                    goto Label_0170;
                case -7:
                    free = LicenseType.Free;
                    goto Label_0170;
                case -9:
                    free = LicenseType.Given;
                    goto Label_0170;
            }
            //如果没有跳转,那又完蛋了
            if (result <= 0)
            {
                free = LicenseType.Free;
                return LicenseType.Free;
            }
            free = LicenseType.Trial;
        }
        else if (ApplicationConfig.Version == 1){……}
        else if (ApplicationConfig.Version == 2){……}
    
    //假设大家都成功跳转过来了,在这里见到大家表示很高兴,如果没有来到这里,在VS里面多调试一下代码。
    Label_0170:
        // 这个程序不是Server,所以不管
        if (regType == RegisterProduct.Server) path = str3;
        //因为我们是LicenseType.Enterprise,所以要执行下面的语句
        if (free != LicenseType.Trial)
        {
            //设置许可类型为free,此时free已为LicenseType.Enterprise
            R.Instance.SetLicenseType(free);
            //将这个有效的SN写入到注册文件,以便下次检测
            File.WriteAllText(path, sn);
            //已经return了,后面不管了
            return free;
        }
        ……
    }
    catch
    {
        type2 = LicenseType.Free;
    }
    finally
    {
        ……
    }
    return type2;
}
  05.修改调试
下面是在VS中生成示例注册码用到的函数,给大家参考一下:
[C#] 纯文本查看 复制代码
//获取加密后的机器码
internal string MCToR()
{
    string s = string.Format("{0}{1}{2}{3}", new object[] { "a71z", this.GetCpuId(), "a91z", this.GetHDid() });
    using (MD5 md = new MD5CryptoServiceProvider())
    {
        byte[] buffer = md.ComputeHash(Encoding.UTF8.GetBytes(s));
        StringBuilder builder = new StringBuilder(0x20);
        for (int i = 0; i < buffer.Length; i++)
        {
            builder.Append(buffer[i].ToString("x").PadLeft(2, '0'));
        }
        return builder.ToString();
    }
}

//获取CUP序列号
internal string GetCpuId()
{
    try
    {
        string str = string.Empty;
        using (ManagementClass class2 = new ManagementClass("Win32_Processor"))
        {
            foreach (ManagementObject obj2 in class2.GetInstances())
            {
                if (obj2.Properties["ProcessorId"] != null)
                {
                    str = obj2.Properties["ProcessorId"].Value.ToString();
                }
                obj2.Dispose();
            }
        }
        return str;
    }
    catch
    {
        return this.GetSystemName();
    }
}

//获取系统名字
internal string GetSystemName()
{
    try
    {
        string str = string.Empty;
        using (ManagementClass class2 = new ManagementClass("Win32_Processor"))
        {
            foreach (ManagementObject obj2 in class2.GetInstances())
            {
                if (obj2.Properties["SystemName"] != null)
                {
                    str = obj2.Properties["SystemName"].Value.ToString();
                }
                obj2.Dispose();
            }
        }
        return str;
    }
    catch (Exception)
    {
        return "SystemName";
    }
}

//获取硬盘序列号
internal string GetHDid()
{
    string str = string.Empty;
    using (ManagementClass class2 = new ManagementClass("Win32_DiskDrive"))
    {
        using (ManagementObjectCollection.ManagementObjectEnumerator enumerator = class2.GetInstances().GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                ManagementObject current = (ManagementObject)enumerator.Current;
                if (current.Properties["Model"] != null)
                {
                    str = (string)current.Properties["Model"].Value;
                }
                current.Dispose();
            }
        }
    }
    if (str != null)
    {
        str.ToString();
    }
    return str;
}

//将字符串转为整型
public static bool TryParse(string s, out int result)
{
    return Int32.TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}

//转MD5
public static string ToMD5(string KEY)
{
    byte[] bytes = Encoding.Default.GetBytes(KEY);
    MD5 md = new MD5CryptoServiceProvider();
    return BitConverter.ToString(md.ComputeHash(bytes)).Replace("-", "").Substring(0, 8);
}

//加密函数
public  string Encode(string data, string KEY_64, string IV_64)
{
    KEY_64 =ToMD5(KEY_64);
    IV_64 = ToMD5(IV_64);
    byte[] bytes = System.Text.Encoding.ASCII.GetBytes(KEY_64);
    byte[] bytes2 = System.Text.Encoding.ASCII.GetBytes(IV_64);
    string result;
    using (System.Security.Cryptography.DESCryptoServiceProvider dESCryptoServiceProvider = new System.Security.Cryptography.DESCryptoServiceProvider())
    {
        int arg_34_0 = dESCryptoServiceProvider.KeySize;
        using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
        {
            using (System.Security.Cryptography.CryptoStream cryptoStream = new System.Security.Cryptography.CryptoStream(memoryStream, dESCryptoServiceProvider.CreateEncryptor(bytes, bytes2), System.Security.Cryptography.CryptoStreamMode.Write))
            {
                System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(cryptoStream);
                streamWriter.Write(data);
                streamWriter.Flush();
                cryptoStream.FlushFinalBlock();
                streamWriter.Flush();
                result = System.Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
            }
        }
    }
    return result;
}

//解密函数
public static string Decode(string data, string KEY_64, string IV_64)
{
    byte[] buffer3;
    string str;
    KEY_64 = ToMD5(KEY_64);
    IV_64 = ToMD5(IV_64);
    byte[] bytes = Encoding.ASCII.GetBytes(KEY_64);
    byte[] rgbIV = Encoding.ASCII.GetBytes(IV_64);
    try
    {
        buffer3 = Convert.FromBase64String(data);
    }
    catch
    {
        return null;
    }
    using (DESCryptoServiceProvider provider = new DESCryptoServiceProvider())
    {
        using (MemoryStream stream = new MemoryStream(buffer3))
        {
            using (CryptoStream stream2 = new CryptoStream(stream, provider.CreateDecryptor(bytes, rgbIV), CryptoStreamMode.Read))
            {
                str = new StreamReader(stream2).ReadToEnd();
            }
        }
    }
    return str;
[align=left][color=rgb(0, 0, 0)][font=verdana, Arial, Helvetica, sans-serif]}
 
用VS生成了一个注册码,进行注册后,终于显示成功,有点小激动!
06.测试程序
猜中了开头,没有猜中故事的结局,重启后出现这个警告,并且企业版又变回了试用版,点击确定后,程序关闭。
搜索"非法授权"关键字,找到了SN.OnlineCheck()函数(在不能修改的AZMap.Core.dll文件中),认识英文的都知道,它是联网查询数据库检查是否存在 这个SN,接下来怎么办?黑数据库?……
return MySQLHelper.CheckMCExisted(R.smethod_0());
开个玩笑,我们接下来分析一下,它出现的位置。
柳暗花明又一村,这是在AZMap.exe中调用的,这就好办了,修改判断啊,来个狠一点,这个timer的Tick事件一直会检测SN是否合法,检查加密狗是否插入、拔出、合法,直接全删除!
终于写完了,效果图!
四、注意事项
01.本教程不提供最终成品,已分析了详细过程,不要因此而留言;
02.在分析和调试时AZMap.Core.dll应使用脱壳后的,在检查SN是否有效则使用原版AZMap.Core.dll;
03. 大家快来评分吧,版主来加精吧。

免费评分

参与人数 23吾爱币 +2 热心值 +23 收起 理由
南丁白羊 + 1 + 1 我很赞同!
starh + 1 + 1 谢谢@Thanks!
sxhytds + 1 谢谢@Thanks!
sxsy88 + 1 我很赞同!
yber + 1 我很赞同!
咚次哒次 + 1 我很赞同!
mgnptlv + 1 谢谢@Thanks!
dkepxs + 1 好贴,希望多发点NET的文章,工具能放个网盘.
dddou2000 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
linzi + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
heimu360 + 1 我很赞同!
夜话飘渺 + 1 谢谢@Thanks!
452048928@qq.co + 1 我很赞同!
q348114971 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
Peerless + 1 谢谢@Thanks!
netle8 + 1 已答复!
盈盈一水间cc + 1 猜中了开头,没有猜中故事的结局:D
hpy1994 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
tzxinqing + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
yicong135 + 1 和楼主同意.net菜鸟,碰到壳搞不定的就没办.
死神哇 + 1 我很赞同!
白吱声 + 1 有人!
Avenshy + 1 没人?

查看全部评分

本帖被以下淘专辑推荐:

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

 楼主| 我也是个傻瓜 发表于 2015-10-7 14:08
dengcaijun 发表于 2015-10-7 08:26
试用了一天,太乐地图功能还是薄弱了点,下载的影像做投影转换,连最基本的用七参数转换都没有,经纬度的保 ...

http://www.cnblogs.com/liweis/p/4575656.html

点评

《那些年....》也是你写的啊?  发表于 2015-10-21 13:55
dengcaijun 发表于 2015-10-7 20:44
我也是个傻瓜 发表于 2015-10-7 14:08
http://www.cnblogs.com/liweis/p/4575656.html

呵呵,原来那些年用过的地图下载器作者是你之前看过这篇文章,推荐的不错。
我是施工的,GIS完全是门外汉,纯属兴趣爱好。最近我在搞个影像(50km×10km范围)与CAD配准。
之前用稻歌,黑块太多了,放弃了。后来用谷地,试用版功能限制太多了,但CAD导入后与地球配准效果还是不错的。后来用bigmap,傻傻花了800块,只能下载影像和投影转换,其他功能实在薄弱。昨天用了下太乐,投影转换功能实在弱。有个Mobile Atlas Creator ,和你推荐的全能电子地图下载器非常相似,下载影像非常不错,可惜不支持Tiff格式。水经注没用过。locaspaceview好像还行,免费的,用了两次卸载了。
现在我就相信ARCGIS 和GlobalMapper,做投影转换应该是权威。
大神,能否把bigemap破了,看他们推广的很神乎,不知道企业版还有些什么功能。或者大神给推荐个能下TIFF格式的软件?
双木 发表于 2015-8-29 23:59
spguangz 发表于 2015-8-30 00:03
很详细 先收藏作参考
8782146 发表于 2015-8-30 00:04
这个NET的分析确实很详细
倾世无双 发表于 2015-8-30 00:10
厉害啊!!
lovern70 发表于 2015-8-30 01:07
分析的很详尽,可惜我看不懂
自由の守护者 发表于 2015-8-30 01:15 来自手机
看不懂 1
glionying 发表于 2015-8-30 03:57
很不错的教程
3yu3 发表于 2015-8-30 08:36
我也研究研究。。。
Hmily 发表于 2015-9-2 12:39
不错,一直在做技术分享,加精鼓励,期待更多精彩!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-17 01:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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