某财务记账软件网络版 -- 追码/加密狗破解
本帖最后由 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
很早之前也弄过。。
还是不对,我把文件拆开看了,用户名不能是这个列表之外的名称。
public static List<string> List_0
{
get
{
if (list_0 == null)
{
list_0 = new List<string>();
list_0.Add("合发机械工程(吉打)有限公司");// 只有这一个客户..
}
return list_0;
}
}
所以注册机并没有什么用。 很好,学习了!!! 挺不错的吗 不错谢谢!很详细 很高深,还是不懂啊,努力学习吧 net源码原来可以反编译.. 感觉能够分析.net 很强大,学习中。 没有成品分享 感觉有点复杂的样子哈 不知道是否好用