吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 81602|回复: 206
收起左侧

[.NET逆向] 某财务记账软件网络版 -- 追码/加密狗破解

    [复制链接]
爱飞的猫 发表于 2015-9-12 06:04
本帖最后由 jixun66 于 2015-9-12 23:19 编辑

前言
本来是打算做个注册机的,结果分析到后面发现注册机不可能,于是变成追码。
参考阅读:某财务记账软件网络版 破解永久授权【999用户】版 (http://www.52pojie.cn/thread-411536-1-1.html)

文章可能有所遗漏,欢迎回复;我会尽量补全到文章。
这两天有点头痛,可能无法准时一一作答;嘛。

注册之后的样子:
register.png

准备
反编译:.net Reflector
反混淆:de4dot
辅 助:Visual Studio 2008 / 新版本
目 标:[官方网站] xingyusoft.net,自行寻找下载地址。

小知识
>> 在 .net 的世界,除了 dll 可以动态引入之外,exe 也可以被当做动态链接库调用。

一、摸索注册相关信息
安装目标,反混淆程序,喂给 Reflector;

右键模块,选择「Go To Entry Point」进入入口点:
[C#] 纯文本查看 复制代码
[STAThread]
private static void Main(string[] args)
{
    if (!App.IsFromNet())
    {
        // 重啓程序時關閉之前的進程
        // 需要重啓的時候會將自己的進程 ID 傳入
        if (args.Length > 0)
        {
            int result = 0;
            int.TryParse(args[0], out result);
            if (result > 0)
            {
                App.KillProcess(result, "");
                Thread.Sleep(0x7d0);
            }
        }

        // 禁止重覆運行
        if (!AppSingle.IsMutextExist(Application.StartupPath))
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Class4.nQoeHEmz06gjy();       // 可删除
            Application.Run(new Main());  // 载入主窗口
        }
        else
        {
            MessageBox.Show(string.Format("{0} 正在运行!", SysCfg.AppCaption), "提醒!", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
            Application.Exit();
        }
    }
}



跟进主窗口,寻找「注册」按钮以及相关事件:
[C#] 纯文本查看 复制代码
// 代码省略 ..

this.toolReg.Caption = "注册";
this.toolReg.Glyph = (Image) manager.GetObject("toolReg.Glyph");
this.toolReg.Id = 8;
this.toolReg.Name = "toolReg";
this.toolReg.PaintStyle = BarItemPaintStyle.CaptionGlyph;
item2.Text = "输入您购买的注册码进行注册\r\n或者查看如何购买说明";
tip2.Items.Add(item2);
this.toolReg.SuperTip = tip2;
this.toolReg.ItemClick += new ItemClickEventHandler(this.toolReg_ItemClick);  // 绑定单击事件到函数

// 代码省略 ..

    private void toolReg_ItemClick(object sender, ItemClickEventArgs e)
    {
        dlgReg.Show();    // 展示注册窗口
    }

// 代码省略 ..


单击「dlgReg」进入注册窗口代码,主要关注一下注册部分。
我在这里把变量名稍微改了下方便阅读:
[C#] 纯文本查看 复制代码
private void buttonReg_Click(object sender, EventArgs e)
{
    if (!DevFunc.TextEditMsgEmpty(this.memoEditSN, "请输入您的注册码!"))
    {
        bool bFirstValidation = true;
        int nRegType = 0;
        string strKeyID = "";
        string strName = "";
        string strSN = "";
        int nLicenseCount = 0;

        // 授权起始、结束
        string sSubscriptionBegin = "";
        string sSubscriptionEnd = "";

        // 清理空白字符
        string strText = this.memoEditSN.Text.Trim().Replace(' ', ' ').Replace('\t', ' ').Replace(" ", "");

        if (strText.Contains("TYPE:") && strText.Contains("加密锁ID:"))
        {
            Dictionary<string, string> regInfo = DlStr.ParseLineToKV(strText, ':');
            // 类型, jixun66@LCG
            if (regInfo.ContainsKey("TYPE"))
                int.TryParse(regInfo["TYPE"], out nRegType);

            if (regInfo.ContainsKey("加密锁ID"))
                strKeyID = regInfo["加密锁ID"];

            // 如果不存在则永久授权
            // 为了方便,不深入这部分代码。
            if (regInfo.ContainsKey("服务期限"))
            {
                string[] strArray = regInfo["服务期限"].Split(new char[] { '至' });
                if (strArray.Length >= 2) {
                    sSubscriptionBegin = strArray[0];
                    sSubscriptionEnd = strArray[1];
                }
            }

            // 如果为 -1 或 不限制 则不限制。
            if (regInfo.ContainsKey("连接数量"))
            {
                strText = regInfo["连接数量"];
                if (strText == "不限制") nLicenseCount = -1;
                else int.TryParse(strText, out nLicenseCount);
            }

            // 注册名
            if (regInfo.ContainsKey("注册名"))
            {
                strName = regInfo["注册名"];
            }

            // 注册码
            if (regInfo.ContainsKey("注册码"))
            {
                strSN = regInfo["注册码"];
            }

            // 简单检查一下序列号的合法性
            if (
                // nRegType 必须为 1 或 2
                ((nRegType != 1) && (nRegType != 2))

                // strKeyID、strName 以及 strSN 必须被赋值
                || (((strKeyID.Length == 0) || (strName.Length == 0)) || ((strSN.Length == 0) 

                // 授权数目必须为 -1 或大于 1
                || ((nLicenseCount != -1) && (nLicenseCount < 2)))))
            {
                bFirstValidation = false;
            }
        }
        else
        {
            // 忽略,不深入这里。
        }

        // 若是第一层的简单检测通过,则保存注册信息。
        if (bFirstValidation)
        {
            VerifyHandler.SaveRegInfo(nRegType, strKeyID, strName, strSN, nLicenseCount, sSubscriptionBegin, sSubscriptionEnd);
        }

        // 提示重启验证。
        base.DialogResult = DialogResult.OK;
        PublicFunc.MsgSuccess("请重新启动软件系统进行验证!");
        Main.Instance.PrepairToExit();

        // 重新启动,带上自己的进程 ID 供新产生的进程结束
        DlFile.ExecFile(Application.ExecutablePath, false, new string[] { Process.GetCurrentProcess().Id.ToString() });
    }
}


现在可以看出注册码的格式了,我们来构建一个让程序读进去:
TYPE:1
加密锁ID:52pojie
服务期限:2012-12-21至2099-12-21
连接数量:998
注册名:jixun@LCG
注册码:你的序列号

程序自动重启,观察注册表可以发现相关内容被写入 (虽然是加密的):
registry.png
* 注册表内容在「HKEY_LOCAL_MACHINE\SOFTWARE\NvidiaTools」里面。

二、重启验证
回到主窗口的代码,搜索「试用版」之类的关键字,定位到「method_5」这个方法:
[C#] 纯文本查看 复制代码
// xyCashManageNetServer.Main
// 主窗口里面

private bool method_5()
{
    // 代码省略 ...
    bool bIsLicensed = false;

    // 验证加密狗,看到这么多调用懒得看了
    // 有兴趣可以跟进去模拟个狗出来
    if (VerifyHandler.IsDogExist())
    {
        if (VerifyHandler.DoDogVerify() && VerifyHandler.GetRegInfo(ref SysCfg.m_nRegType, ref SysCfg.m_strKeyID, ref SysCfg.m_strRegUser, ref NetServer.m_nLimitedCount, ref SysCfg.m_dtReg1, ref SysCfg.m_dtReg2))
        {
            bIsLicensed = true;
        }
    }
    // 无狗时的验证
    else if (VerifyHandler.IsReg_ServerWithoutDog(ref SysCfg.m_strRegUser, ref NetServer.m_nLimitedCount))
    {
        SysCfg.m_nRegType = 0;
        bIsLicensed = true;
        SysCfg.m_bUseDog = false;
    }

    if (bIsLicensed)
    {
        // 这段应该是授权时间的检查?
        // 头疼,不用管。
        if (
            // 当注册类型为 1 或 2
            ((SysCfg.m_nRegType == 1) || (SysCfg.m_nRegType == 2))

            // 并且 VersionDate > m_dtReg2
            && (SysCfg.VersionDate > SysCfg.m_dtReg2))
        {
            PublicFunc.MsgError("对不起,您无权使用此版本!如果您要继续使用此版本,请获得此版本的授权!");
            new dlgReg().ShowDialog();
            return false;
        }

        // 目标
        this.statusTryDays.Caption = "您已经注册成功!";
        (this.statusTryDays.SuperTip.Items[0] as ToolTipItem).Text = "您已经注册成功,当前是正式版!";
        SysCfg.m_bReg = true;
        return true;
    }

    // 试用版通知,代码省略 ..
}


目标很明确,让「VerifyHandler.IsReg_ServerWithoutDog」返回「true」,函数内容如下:
[C#] 纯文本查看 复制代码
public static bool IsReg_ServerWithoutDog(ref string strName, ref int nCount)
{
    // 分别读入注册表的密文,
    // 用户名、序列号、授权数目(* 无限制时的值为 1000,算号时要填对)
    string strUsername = ReadName(true);
    string strSerial = ReadSN(true);
    string strCount = ReadCount();
    
    // RegCommon.Instance.method_1
    // 这个是解密注册表内容到明文,不用管。
    strUsername = RegCommon.Instance.method_1(strUsername);
    strSerial = RegCommon.Instance.method_1(strSerial);
    strCount = RegCommon.Instance.method_1(strCount);
    
    // List_0 是一个购买了正版的用户列表
    // 里面只有一个用户,所以只能实现追码而非注册机。
    if (Const.List_0.Contains(strUsername))
    {
        strUsername = strUsername + strCount + "C2DF732065C3ACBF04BBE109D39FEA47";
        Class2 class2 = new Class2(); // 暂定名称为 Encoder,method_3 命名为 encodeString
        strUsername = class2.method_3(strUsername, "DDE97A3B7CDEB3C50FB2FC03EEA6E59D");
        strUsername = class2.method_3(strUsername, "DDE97A3B7CDEB3C50FB2FC03EEA6E59D");
        
        // 此处参见解说,追码的关键
        strUsername = class2.method_3(strUsername, "DDE97A3B7CDEB3C50FB2FC03EEA6E59D");
        strSerial   = class2.method_3(strSerial,   "DDE97A3B7CDEB3C50FB2FC03EEA6E59D");
        
        if (strUsername == strSerial)
        {
            strName = strUsername;
            int.TryParse(strCount, out nCount);
            return true;
        }
    }
    return false;
}


* 修订:我到加密狗官网下了一份开发手册看了下,其实是 TEA 加密算法。下面的加密狗分析的时候会提到。
其中,「method_3」是加密字符串 (不足位补 0),「method_4」是解密字符串。

「class2.method_3」是一个神奇的东西,具体干嘛的我也不知道,大体就是加密数据然后编码到十六进制。

但是! 就相当于 MD5 这类哈希函数一样,如果输入内容一致,则产生的结果一定也是一致的。

简单来说,「相加 (1, 1)」不管执行多少次,它的结果永远是「2」这个恒定值。

同等原理,「strUsername」在执行到注释那行的时候,就是序列号的明文

三、制作逆向辅助
打开 VS,糊个界面,然后加入算号代码:
reverse_helper.png
[C#] 纯文本查看 复制代码
private void btnGenerate_Click(object sender, EventArgs e)
{
    // jixun66@LCG
    var strSerial = textUser.Text + textCount.Text + "C2DF732065C3ACBF04BBE109D39FEA47";

    var encoder = new Encoder();
    strSerial = encoder.encodeString(strSerial, "DDE97A3B7CDEB3C50FB2FC03EEA6E59D");
    strSerial = encoder.encodeString(strSerial, "DDE97A3B7CDEB3C50FB2FC03EEA6E59D");

    textSerial.Text = strSerial;
}


运行,算号,算出来的放到「注册码」区域后喂给程序验证就好了。

附、引入应用程序作为模块调用
项目 -> 添加引用 -> 浏览 -> 反混淆后的文件(记得改到原始文件名,不然会提示找不到)
add_reference.png
browse_reference.png

[C#] 纯文本查看 复制代码
private void button1_Click(object sender, EventArgs e)
{
    int m_nLimitedCount = 0;
    string m_strRegUser = "";
    bool flag = xyCashManageNet.Reg.Verify.VerifyHandler.IsReg_ServerWithoutDog(ref m_strRegUser, ref m_nLimitedCount);
    // 此处应有断点
    nullptr();
}
private void nullptr() { }


某序列号.txt (477 Bytes, 下载次数: 300)

附、加密狗破解
今天头不怎么痛了,研究一下加密狗的注册。

还是在「xyCashManageNetServer.Main.method_5」这个函数里面:
[C#] 纯文本查看 复制代码
// 代码省略 ...
if (VerifyHandler.IsDogExist())
{
	if (VerifyHandler.DoDogVerify()
		// m_nRegType: 注册类型
		// m_strKeyID: KeyID
		// m_strRegUser: 用户名
		// m_nLimitedCount: 连接限制
		// m_dtReg1: 服务期限开始
		// m_dtReg2: 服务期限结束
		&& VerifyHandler.GetRegInfo(ref SysCfg.m_nRegType, ref SysCfg.m_strKeyID, ref SysCfg.m_strRegUser, ref NetServer.m_nLimitedCount, ref SysCfg.m_dtReg1, ref SysCfg.m_dtReg2))
	{
		flag = true;
	}
}
// 代码省略 ...


针对加密狗的检测,首先检查是否存在,然后对狗进行检查。验证函数如下:

[C#] 纯文本查看 复制代码
public static bool IsDogExist()
{
    return DogCommon.IsKeyExist();  // 加密狗是否存在
}

public static bool DoDogVerify()
{
    if (!DogCommon.IsKeyExist())  // 加密狗是否存在
    {
        return false;
    }
    if (!VerifyMemory())  // 验证加密狗的数据读取功能
    {
        return false;
    }
    if (!VerifyWritePassword()) // 验证密文写入
    {
        return false;
    }
    if (!VerifyAlgorithmGeneral()) // 加密狗算法验证 1
    {
        return false;
    }
    if (!VerifyAlgorithmGeneral_2()) // 加密狗算法验证 2
    {
        return false;
    }
    return true; // 如果全部验证通过,则加密狗验证通过。
}


知道了加密狗的校验流程后,点进去依次查看:

首先是查询加密狗是否插入的部分:
[C#] 纯文本查看 复制代码
public static bool IsKeyExist()
{
    StringBuilder outKeyPath = new StringBuilder("", 256);
    if (!FindKey(outKeyPath)) // 检测的同时记录 USB 设备路径
    {
        return false;
    }
    string_0 = outKeyPath.ToString();
    return true;
}

public static bool FindKey(StringBuilder OutKeyPath)
{
    return (FindPort_2(0, 0x351a5e8, 0x5ed8fa94, OutKeyPath) == 0);
}

[DllImport("SYUNEW3D.dll")]
public static extern int FindPort_2(int start, int in_data, int verf_data, StringBuilder OutKeyPath);


通过这段代码得知,加密狗的「FindPort_2」函数必须返回 0;然而对于第四个参数并没有什么头绪,对文档进行查阅,得知函数的原型如下:
参数4――OutPath(out);如果系统中存在第N个符合指定条件的加密锁,则该参数中包含有该加密锁所在的设备路径

那就好办了,可以随意写一些数据进去,或者干脆不管返回 0 就可以了。


继续看下一个函数,VerifyMemory:
[C#] 纯文本查看 复制代码
public static bool VerifyMemory()
{
    if (DogCommon.ReadString(7, Const.ServerMemory.Length) != Const.ServerMemory) // 从地址 7 开始读取一部分数据并检查
    {
        return false;
    }
    if (DogCommon.ReadString(184, Const.ClientMemory.Length) != Const.ClientMemory) // 从地址 184 开始读取一部分数据并检查
    {
        return false;
    }
    return true;
}

public static string ReadString(int nAddress, int nLen)
{
    StringBuilder outstring = new StringBuilder("") {
        Length = nLen
    };

    // 调用加密狗 API 进行检查。
    if (YReadString(outstring, (short) nAddress, (short) nLen, Const.ReadHKey, Const.ReadLKey, string_0) != 0)
    {
        return "";
    }
    return outstring.ToString();
}

[DllImport("SYUNEW3D.dll")]
public static extern int YReadString(StringBuilder outstring, short Address, short mylen, string HKey, string LKey, string KeyPath);


单击「ServerMemory」以及「ClientMemory」进去,可以看到是读取该类的「string_x」变量。这里有一个小技巧快速找到相应的值:
首先点进「string_x」,我这里是 4。
然后在左侧的对象浏览器,右键该成员:

右键 - 分析

右键 - 分析


此时应该能看到一个叫做「Analyzer」的窗口出现,展开「Assigned By」就能找到在何处被赋值。
再右键,选择「Go To Member」定位到相关函数:

展开 - 被赋值

展开 - 被赋值


[C#] 纯文本查看 复制代码
static Const()
{
    string_0 = "0916E6A8"; // 读取密码的高位
    string_1 = "E8FAE0E2"; // 读取密码的低位
    string_2 = "F36690F8"; // 写入密码的高位
    string_3 = "824ADA32"; // 写入密码的高位
    string_4 = "C2DF732065C3ACBF04BBE109D39FEA47"; // ServerMemory
    string_5 = "2B3808CA0B2C02049E013B923DF574EC"; // ClientMemory
    string_6 = "6C780DCA0F6D46589E458B927D3974E1"; // TestMemory
}


数据找到后,依次写到模拟狗的相应位置里面:

模拟狗的数据

模拟狗的数据

* Paste Write: 粘贴覆盖当前位置

接下来,进入「VerifyWritePassword」:
[C#] 纯文本查看 复制代码
public static bool VerifyWritePassword()
{
    // 写入数据到狗里面
    if (!DogCommon.WriteString(365, Const.TestMemory))
    {
        return false;
    }
    // 读入狗数据 (刚刚写的内容)
    return (DogCommon.ReadString(365, Const.TestMemory.Length) == Const.TestMemory);
}

public static bool WriteString(int nAddress, string strData)
{
    if (YWriteString(strData, (short) nAddress, Const.String_0, Const.String_1, string_0) != 0)
    {
        return false;
    }
    return true;
}

[DllImport("SYUNEW3D.dll")]
public static extern int YWriteString(string InString, short Address, string HKey, string LKey, string KeyPath);

// ReadString 函数参见上一段的代码片段

* 鼠标悬浮到数字上面,可以选择十进制/十六进制。

这一段可以不用管,我们在内存直接写入、读入相应的数据。

然后就是算法验证,VerifyAlgorithmGeneral:
[C#] 纯文本查看 复制代码
public static bool VerifyAlgorithmGeneral()
{
    return (DogCommon.CalcGeneral(0x351a5e8) == 0x5ed8fa94);
}

public static int CalcGeneral(int nData)
{
    int num = 0;
    if (sWriteEx(nData, ref num, string_0) != 0)
    {
        return -1;
    }
    return num;
}

[DllImport("SYUNEW3D.dll")]
public static extern int sWriteEx(int in_data, ref int out_data, string KeyPath);


查询官方文档,并没有详细说明该算法的运算规则,模拟狗在此处的行为就行不通了;
但是通过对这个 API 的分析,发现只有这一次验证并且已知这一次的验证结果。

因此在模拟狗的时候,直接写死这个值就能绕过加密:

狗验证算法1的调用链

狗验证算法1的调用链

* 上图查询了狗对于这个 API 的调用链

最后一项验证,VerifyAlgorithmGeneral_2:
[C#] 纯文本查看 复制代码
public static bool VerifyAlgorithmGeneral_2()
{
    Class2 class2 = new Class2();
    string str = "what is this";
    string str2 = class2.method_3(str, "DDE97A3B7CDEB3C50FB2FC03EEA6E59D"); // TEA.encodeString(明文, 密码)
    string str3 = DogCommon.CalcGeneralEnc_2(str);
    return (str2 == str3); // 若是加密狗的运算数据与程序的 TEA 计算结果相同,则验证通过。
}

public static string CalcGeneralEnc_2(string strData)
{
    StringBuilder outstring = new StringBuilder {
        Length = 3000
    };
    if (EncString(strData, outstring, string_0) != 0)
    {
        return "";
    }
    return outstring.ToString();
}

[DllImport("SYUNEW3D.dll")]
public static extern int EncString(string InString, StringBuilder outstring, string KeyPath);


class2 是 TEA 算法这个事实是我在翻阅官方文档后得到的结论:
EncString函数
int  EncString(  char *InString,char *OutString,char *Path);

功能:使用增强算法一对字符串进行加密
...
提示1:增强算法是一个标准的TEA算法,EncString函数与TEA算法的加密过程相对应
...

现在可以开始写狗的模拟了,首先是狗数据导出到 C++ 的格式方便调用:
xxd -i dog_data > dog_data.cpp

狗数据导出

狗数据导出


最下面加上一个函数方便其它文件调用,就不用把狗数据和代码全部塞到一起了(.h 文件也要定义哦):
[C++] 纯文本查看 复制代码
// unsigned char* get_dog_data();
unsigned char* get_dog_data(){
	return dog_bin;
}


在「fake_dog.h」定义需要导出的函数:
[C++] 纯文本查看 复制代码
// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the FAKE_DOG_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// FAKE_DOG_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#define FAKE_DOG_API extern "C" __declspec(dllexport) int __stdcall

FAKE_DOG_API FindPort_2(  int start,DWORD in_data,DWORD verf_data,char *OutPath); 
FAKE_DOG_API YReadString(char *outstring ,short Address,int Len,char * HKey,char *LKey,char *Path );
FAKE_DOG_API EncString(  char *InString,char *OutString,char *Path);
FAKE_DOG_API YWriteString(char *InString,short Address,char * HKey,char *LKey,char *Path );
FAKE_DOG_API sWriteEx(  DWORD in_data,DWORD *out_data,char *Path);
FAKE_DOG_API YReadEx(unsigned char *OutBuf,short address,short len,char *Hkey,char *Lkey,char *Path );

void init_fake_dog();


// 模拟加密狗 DLL 的核心代码,写到「fake_dog.cpp」里面:
[C++] 纯文本查看 复制代码
// fake_dog.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "fake_dog.h" // 导出函数的定义
#include "dog_data.h" // 狗数据

unsigned char* lpDogData = NULL; // 狗数据
void LCG_WATERMARK(){
	Sleep(90000); // 一分半
	MessageBox(NULL, L"加密狗破解 by Jixun\n\n"
		L"仅供学习交流之用,\n"
		L"请勿用于商业用途!\n"
		L"\n"
		L"如果,您喜欢该软件,\n"
		L"请支持作者购买正版!",

		L"Jixun // LCG", MB_ICONINFORMATION);
}
void init_fake_dog(){
	lpDogData = get_dog_data();

	// 随机水印
	if (rand() % 100 > 70) {
		CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)LCG_WATERMARK, NULL, NULL, NULL);
	}
}

FAKE_DOG_API FindPort_2(  int start,DWORD in_data,DWORD verf_data,char *OutPath) {
	char lpCustomPath[] = "\\FAKE_USB_DOG\\By\\Jixun";
	memcpy(OutPath, lpCustomPath, sizeof lpCustomPath);
	return 0;
}

FAKE_DOG_API YReadString(char *outstring ,short Address,int Len,char * HKey,char *LKey,char *Path ) {
	memcpy(outstring, lpDogData + Address, Len);
	return 0;
}

FAKE_DOG_API EncString(  char *InString,char *OutString,char *Path) {
	// TEA 编码,不怎么会弄
	// 但是这样也能过验证,所以就不管了
	char szData[] = "7B2BDB1E7DDAD6E37468697300";
	memcpy(OutString, szData, sizeof szData);
	return 0;
}

FAKE_DOG_API YWriteString(char *InString,short Address,char * HKey,char *LKey,char *Path ) {
	memcpy(lpDogData + Address, InString, lstrlenA(InString));
	return 0;
}

FAKE_DOG_API sWriteEx(  DWORD in_data,DWORD *out_data,char *Path){
	*out_data = 0x5ed8fa94;
	return 0;
}

FAKE_DOG_API YReadEx(unsigned char *OutBuf,short address,short len,char *Hkey,char *Lkey,char *Path ) {
	memcpy(OutBuf, lpDogData + address, len);
	return 0;
}


最后,在 DLL 初始化的时候初始化假狗的数据(dll_main.cpp):
[C++] 纯文本查看 复制代码
	case DLL_PROCESS_ATTACH:
		init_fake_dog();


此时,「DoDogVerify」的验证已经能够通过了,接下来是读取注册信息的代码了。

[C#] 纯文本查看 复制代码
public static bool GetRegInfo(ref int nType, ref string strKeyID, ref string strName, ref int nCount, ref DateTime dt1, ref DateTime dt2)
{
    string strSN = "";
    string str2 = "";
    string str3 = "";
    string strCount = "";
	
	// 从注册表读入用户之前键入的数据
    if (!RegCommon.Instance.ReadRegInfo(ref nType, ref strKeyID, ref strName, ref strSN, ref strCount, ref str2, ref str3))
    {
        return false;
    }
	
	// 进行解密
    strName = RegCommon.Instance.method_1(strName);
    strSN = RegCommon.Instance.method_1(strSN);
    strCount = RegCommon.Instance.method_1(strCount);
    if (!int.TryParse(strCount, out nCount))
        return false;
	
    if (nType == 0)
    {
        strName = strName + strCount + Const.ServerMemory;
		
		// 在狗上进行 TEA 加密运算。
		// 
		// 还记得吗? 我们的模拟狗让这个函数自动返回一个固
		// 定值但是程序并没有针对返回内容进行检测,只是检
		// 测了两个函数对一串字符串的结果是否相等。
        strName = DogCommon.CalcGeneralEnc_2(strName);
        strName = DogCommon.CalcGeneralEnc_2(strName);
        strName = DogCommon.CalcGeneralEnc_2(strName);
        strSN   = DogCommon.CalcGeneralEnc_2(strSN  );
		
        string strName2  = "";
        string strDate2  = "";
        string strKeyID2 = "";
		// 读取内存区域信息, 分别为用户名、日期、注册狗 ID
		// 这些不重要,重要的是这个函数的返回值必须为 1。
        if (GetMemoryInfo(ref strName2, ref strDate2, ref strKeyID2) != 1)
        {
            return false;
        }
        return (strName == strSN);
    }
	
	// 代码省略, 上面有让函数返回 True 的位置
}

public static int GetMemoryInfo(ref string strName, ref string strDate, ref string strKeyID)
{
    if (!DogCommon.IsKeyExist())
        return -1;
	
    byte[] outData = new byte[Const.MemoryLength];
    if (!DogCommon.ReadHex(outData, Const.MemoryAddress))
        return -1;
	
    int num = 0;
    byte[] buffer2 = outData;
	// 从 MemoryAddress(50) 开始读入 MemoryLength(122) 个字节
	// 若是内容全部为 0,则返回 1。
    for (int i = 0; i < buffer2.Length; i++)
        if (outData[0] == 0)
            num++;
	
    if (num == outData.Length)
        return 1;
	
	// 代码省略,后面没有返回 1 的。
}

public static bool ReadHex(byte[] OutData, int nAddress)
{
    return (YReadEx(OutData, (short) nAddress, (short) OutData.Length, Const.ReadHKey, Const.ReadLKey, string_0) == 0);
}

[DllImport("SYUNEW3D.dll")]
public static extern int YReadEx(byte[] OutData, short Address, short mylen, string HKey, string LKey, string KeyPath);


结果发现就算走加密狗验证路线,也需要输入一些数据进去;但是因为验证方面的漏洞,导致可以任意用户名、注册码激活软件。

输入随便刚构建的注册码,可以用,激活成功。
模拟狗代码 (C , VS 2008).rar (27.54 KB, 下载次数: 806)
用户名:Jixun // LCG
服务器:52pojie.cn
连接数量:不限制

点评

没弄过狗,感觉挺好玩的。  发表于 2015-9-15 14:46

免费评分

参与人数 46吾爱币 +3 热心值 +46 收起 理由
ziye + 1 + 1 谢谢@Thanks!
pizza100 + 1 + 1 谢谢@Thanks!
谷子 + 1 + 1 用心讨论,共获提升!
zhijane + 1 我很赞同!
随风飘缈 + 1 我很赞同!
610100 + 1 佩服!
牛人小白 + 1 我很赞同!
52code + 1 谢谢@Thanks!
catboy + 1 想问问楼主能否给一个易芝财务的破解教程
cpsfengjie + 1 热心回复!
lfkof + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
fzx118 + 1 厉害 打狗专家级了 谢谢分享
熊猫京京 + 1 我很赞同!
小人物241 + 1 我很赞同!
北门飘雪 + 1 鼓励转贴优秀软件安全工具和文档!
灰太狼大王 + 1 我很赞同!
934565564 + 1 感谢您的宝贵建议,我们会努力争取做得更好.
wu0687050 + 1 膜拜
zr0081 + 1 鼓励转贴优秀软件安全工具和文档!
guitar08 + 1 我很赞同!
jlbboy + 1 谢谢@Thanks!
125733578 + 1 这NB帖子,不加分都不行
绘梨衣 + 1 我很赞同!
luzener + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
abacd1 + 1 早5年看到这文章就好了~
小木头 + 1 已经处理,感谢您对吾爱破解论坛的支持!
学习啊 + 1 鼓励转贴优秀软件安全工具和文档!
azuresky0819 + 1 鼓励转贴优秀软件安全工具和文档!
Hslim + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
TMTT + 1 不错,留名备用。
Tortoise + 1 谢谢@Thanks!
ljrlove2008 + 1 我很赞同!
peter_king + 1 谢谢@Thanks!
yuluo5566 + 1 我很赞同!
无的世界零 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
jacky520510 + 1 我很赞同!
d586w + 1 热心回复!
迷茫小白 + 1 我很赞同!
dapang + 1 谢谢@Thanks!
无痕软件 + 1 马上这些漏洞就会被修复,哈哈哈
lies2014 + 1 谢谢@Thanks!
wang1234561211 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
yag2007 + 1 膜拜大牛,注册机都做出来了!
三少爷的剑 + 1 好文,好文!
逍遥枷锁 + 1 谢谢@Thanks!
howsk + 1 已答复!

查看全部评分

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

3yu3 发表于 2015-9-12 08:46
很早之前也弄过。。 QQ截图20150912084547.png

点评

期待分享注册机,呵呵  发表于 2015-9-16 19:41
 楼主| 爱飞的猫 发表于 2015-9-12 17:26
3yu3 发表于 2015-9-12 08:46
很早之前也弄过。。

还是不对,我把文件拆开看了,用户名不能是这个列表之外的名称。

[C#] 纯文本查看 复制代码
public static List<string> List_0
{
    get
    {
        if (list_0 == null)
        {
            list_0 = new List<string>();
            list_0.Add("合发机械工程(吉打)有限公司");  // 只有这一个客户..
        }
        return list_0;
    }
}


所以注册机并没有什么用。
头像被屏蔽
281590720 发表于 2015-9-12 06:27
吃西瓜的正太 发表于 2015-9-12 07:34
挺不错的吗
lxj12328 发表于 2015-9-12 08:04
不错  谢谢!很详细
zz100179 发表于 2015-9-12 08:08
很高深,还是不懂啊,努力学习吧
hlrlqy 发表于 2015-9-12 08:17
net源码原来可以反编译..
xlcomputer 发表于 2015-9-12 09:13
感觉能够分析.net 很强大,学习中。
mapeng2050 发表于 2015-9-12 09:14
没有成品分享
yuchen0755 发表于 2015-9-12 09:39
感觉有点复杂的样子哈    不知道是否好用
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-17 02:35

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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