爱飞的猫 发表于 2015-9-12 06:04

某财务记账软件网络版 -- 追码/加密狗破解

本帖最后由 jixun66 于 2015-9-12 23:19 编辑

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

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

注册之后的样子:


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

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

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

右键模块,选择「Go To Entry Point」进入入口点:

private static void Main(string[] args)
{
    if (!App.IsFromNet())
    {
      // 重啓程序時關閉之前的進程
      // 需要重啓的時候會將自己的進程 ID 傳入
      if (args.Length > 0)
      {
            int result = 0;
            int.TryParse(args, 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();
      }
    }
}


跟进主窗口,寻找「注册」按钮以及相关事件:
// 代码省略 ..

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」进入注册窗口代码,主要关注一下注册部分。
我在这里把变量名稍微改了下方便阅读:
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;
                  sSubscriptionEnd = strArray;
                }
            }

            // 如果为 -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
注册码:你的序列号

程序自动重启,观察注册表可以发现相关内容被写入 (虽然是加密的):

* 注册表内容在「HKEY_LOCAL_MACHINE\SOFTWARE\NvidiaTools」里面。

二、重启验证
回到主窗口的代码,搜索「试用版」之类的关键字,定位到「method_5」这个方法:
// 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 as ToolTipItem).Text = "您已经注册成功,当前是正式版!";
      SysCfg.m_bReg = true;
      return true;
    }

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

目标很明确,让「VerifyHandler.IsReg_ServerWithoutDog」返回「true」,函数内容如下:
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,糊个界面,然后加入算号代码:

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;
}

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

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



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() { }



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

还是在「xyCashManageNetServer.Main.method_5」这个函数里面:
// 代码省略 ...
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;
        }
}
// 代码省略 ...

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

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; // 如果全部验证通过,则加密狗验证通过。
}


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

首先是查询加密狗是否插入的部分:

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);
}


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:
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();
}


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」定位到相关函数:


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」:
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;
}


public static extern int YWriteString(string InString, short Address, string HKey, string LKey, string KeyPath);

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

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

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

然后就是算法验证,VerifyAlgorithmGeneral:

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;
}


public static extern int sWriteEx(int in_data, ref int out_data, string KeyPath);


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

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

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

最后一项验证,VerifyAlgorithmGeneral_2:

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();
}


public static extern int EncString(string InString, StringBuilder outstring, string KeyPath);


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

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

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


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

在「fake_dog.h」定义需要导出的函数:
// 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」里面:
// 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):
        case DLL_PROCESS_ATTACH:
                init_fake_dog();

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

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;
    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)
            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);
}


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

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

输入随便刚构建的注册码,可以用,激活成功。


用户名:Jixun // LCG
服务器:52pojie.cn
连接数量:不限制

3yu3 发表于 2015-9-12 08:46

很早之前也弄过。。

爱飞的猫 发表于 2015-9-12 17:26

3yu3 发表于 2015-9-12 08:46
很早之前也弄过。。

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

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

感觉有点复杂的样子哈    不知道是否好用
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 某财务记账软件网络版 -- 追码/加密狗破解