zyyujq 发表于 2025-4-24 11:01

虚拟串口驱动专业版 Eltima Virtual Serial Port Driver Pro 最新中文版本 V11.0.1068 IL代码分析

本帖最后由 zyyujq 于 2025-4-24 11:55 编辑

虚拟串口驱动专业版 Eltima Virtual Serial Port Driver Pro 最新中文版本 V11.0.1068 IL代码分析

运行环境:WIN10、WIN11
涉及工具:dnSpy
教程类型:逆向限制,算法分析等
是否讲解思路和原理:是
以下为图文内容:








主程序 vspdpro.exe 的破解和汉化:


Virtual Serial Port Driver 版本从 V10.0.844开始到现在的V11.0.1068,主程序为 vspdpro.exe,
都采用 MS Visual C# / Basic.NET 代码编译,且没有混淆代码,容易逆向。
破解详解:


1、安装后,使用 dnSpy 打开 主程序 vspdpro.exe
2、破解主程序,dnSpy 打开程序后,可以选择C#、VB、IL代码形式查看源代码


在 vspd.Control 下有 IsDemoKey、IsProKey


A、进入 vspd.Control.IsDemo


public bool IsDemo
{
      
      get
      {
                return this.<IsDemo>k__BackingField;                //注释:此处鼠标右键选择编辑IL指令,即改为return false,返回否,不是演示版。
      }
      
      private set
      {
                if (this.<IsDemo>k__BackingField == value)
                {
                        return;
                }
                this.<IsDemo>k__BackingField = value;
                this.OnPropertyChanged("IsDemo");
      }
}


IsDemo.get 删除多余的IL指令,只留如下:
0      0000      ldc.i4.0          //注解:值0
1      0001      ret                //返回,上面值0,即 false


编辑IL指令后:
public bool IsDemo
{
      
      get
      {
                return false;
      }
      
      private set
      {
                this.<IsDemo>k__BackingField = value;
                this.OnPropertyChanged("IsDemo");
      }
}




B、进入 vspd.Control.IsPro


public bool IsPro
{
      
      get
      {
                return this.<IsPro>k__BackingField;                  //注释:此处鼠标右键选择编辑IL指令,即改为return true,返回是,是专业版。
      }
      
      private set
      {
                if (this.<IsPro>k__BackingField == value)
                {
                        return;
                }
                this.<IsPro>k__BackingField = value;
                this.OnPropertyChanged("IsPro");
      }
}


IsPro.get 删除多余的IL指令,只留如下:
0      0000      ldc.i4.1          //注解:值1
1      0001      ret             //返回,上面值1,即 true






C、点击 vspd.vm.Activator ,下有DaysLimit、IsExpired、IsGoodToWork、IsDemoKey、IsProKey,修改方法同上。


修改 IsExpired 返回 false (未过期),
修改 IsGoodToWork 返回 true(工作OK),
修改 IsDemoKey 返回 false(非演示),
修改 IsProKey 返回 true(是专业版)


进入 vspd.vm.Activator.IsDemoKey


public override bool IsDemoKey
{
      get
      {
                if (!this.license.demo)
                {
                        int? days = this.license.days;
                        int num = 0;
                        return days.GetValueOrDefault() > num & days != null;
                }
                return true;            //注解:将上面if语句删除,将此改为 false ,即 get { return false; } ,IsDemoKey直接返回 false (不是演示版)。
      }
}


在get语句内,鼠标右键选择编辑IL指令,删除多余的指令,修改为如下:


0      0000      ldc.i4.0          //注解:值0
1      0001      ret                //返回,上面值0,即 false




进入 vspd.vm.Activator.DaysLimit


public override long DaysLimit
{
    get
    {
      return (long)this.license.days.GetValueOrDefault();
    }
}


返回可以修改为3650,过期天数为十年:


public override long DaysLimit
{
    get
    {
      return 3650L;
    }
}




鼠标右键选择编辑IL指令,删除多余的指令,修改为如下:


0      0000      ldc.i4    3650      //注解:值3650 (十六进制 0xE42)
1      0001      ret                   //返回,上面值3650,即3650天,十年




D、进入 vspd.Utils.LicenseUpdater ,修改证书更新


public void UpdateLicense()
{
      string text;
      uint num;
      for (;;)
      {
                text = this.Activator.LicenseStatus;
                if (this.Activator.IsDemoKey && this.Activator.IsExpired && text != "+STARTING")
                {
                        text = "DEMO_IS_OVER";
                }
                num = <PrivateImplementationDetails>.ComputeStringHash(text);
                if (num <= 890022063U)
                {
                        if (num > 516577132U)
                        {
                              goto IL_6B;
                        }
                        if (num == 215374295U)
                        {
                              goto IL_DB;
                        }
                        if (num != 516577132U)
                        {
                              break;
                        }
                        if (!(text == "+BADTIME"))
                        {
                              break;
                        }
                        if (!new AlertWindow
                        {
                              FirstLine = Resources.IDSA_badlocaltime,
                              SecondLine = Resources.IDSA_setdatetime,
                              OkButtonText = Resources.IDSA_btn_tryagain
                        }.Run(this.parentWindow))
                        {
                              return;
                        }
                        this.Activator.ActivateOnline("reactivate");
                }
                else if (num <= 2646111894U)
                {
                        if (num == 1181904525U)
                        {
                              goto IL_12F;
                        }
                        if (num != 2646111894U)
                        {
                              break;
                        }
                        if (!(text == "+BACKWARDTIME"))
                        {
                              break;
                        }
                        if (!new AlertWindow
                        {
                              FirstLine = Resources.IDSA_time_backward,
                              SecondLine = Resources.IDSA_trial_blocked,
                              OkButtonText = Resources.IDSA_btn_activate,
                              CancelButtonText = Resources.IDSA_btn_ordernow
                        }.Run(this.parentWindow))
                        {
                              goto IL_20E;
                        }
                        this.Activator.ActivateOnline("demo");
                }
                else if (num != 3427124686U)
                {
                        if (num == 3457219322U)
                        {
                              goto IL_F0;
                        }
                        if (num != 3914721229U)
                        {
                              break;
                        }
                        if (!(text == "+WRONG_HID"))
                        {
                              break;
                        }
                        if (!new AlertWindow
                        {
                              Title = Resources.IDSA_title_reactivation_required,
                              FirstLine = Resources.IDSA_wrong_hid,
                              SecondLine = Resources.IDSA_failed_reactivate,
                              OkButtonText = Resources.IDSA_btn_tryagain,
                              CancelButtonText = Resources.IDSA_btn_upd_info
                        }.Run(this.parentWindow))
                        {
                              goto IL_372;
                        }
                        this.Activator.ActivateOnline("reactivate");
                }
                else
                {
                        if (!(text == "+STARTING"))
                        {
                              break;
                        }
                        Thread.Sleep(500);
                        this.Activator.UpdateStatus();
                }
      }
      goto IL_393;
      IL_6B:
      if (num != 609027880U)
      {
                if (num == 890022063U)
                {
                        if (text == "0")
                        {
                              goto IL_183;
                        }
                }
      }
      else if (text == "NO_AVAILABLE_ACTIVATIONS")
      {
                if (new AlertWindow
                {
                        Title = Resources.IDSA_title_cant_reactivate_anymore,
                        FirstLine = Resources.IDSA_max_hids,
                        SecondLine = Resources.IDSA_msg_maxhids_update.Replace("{update_info_button}", Resources.IDSA_btn_upd_info),
                        OkButtonText = Resources.IDSA_btn_activate,
                        CancelButtonText = Resources.IDSA_btn_upd_info
                }.Run(this.parentWindow))
                {
                        this.<UpdateLicense>g__ShowRegisterWindow|5_1();
                        return;
                }
                this.OpenChangeHidUrl();
                return;
      }
      goto IL_393;
      IL_DB:
      if (!(text == "ALREADY_ACTIVATED"))//已激活
      {
                goto IL_393;
      }
      goto IL_183;
      IL_F0:
      if (text == "DEMO_IS_OVER")
      {
                this.<UpdateLicense>g__ShowWizard|5_0(new WizardModel.Expired());
                return;
      }
      goto IL_393;
      IL_12F:
      if (text == "KEY_BANNED")
      {
                if (new AlertWindow
                {
                        FirstLine = Resources.IDSA_error_key_banned,
                        SecondLine = Resources.IDSA_support_email,
                        OkButtonText = Resources.IDSA_btn_activate,
                        CancelButtonText = Resources.IDSA_btn_ordernow
                }.Run(this.parentWindow))
                {
                        this.<UpdateLicense>g__ShowRegisterWindow|5_1();
                        return;
                }
                this.OpenPurchaseUrl();
                return;
      }
      goto IL_393;
      IL_183:
      if (this.Activator.IsDemoKey || this.Activator.DaysLimit > 0L)
      {
                this.<UpdateLicense>g__ShowWizard|5_0(new WizardModel.Message());
                return;
      }
      return;
      IL_20E:
      this.OpenPurchaseUrl();
      return;
      IL_372:
      this.OpenChangeHidUrl();
      return;
      IL_393:
      if (new AlertWindow
      {
                Title = Resources.IDSA_title_other_error,
                FirstLine = Resources.IDSA_need_activate,
                SecondLine = Resources.IDSA_error_details.Replace("{error}", text),
                OkButtonText = Resources.IDSA_btn_tryagain,
                CancelButtonText = Resources.IDSA_btn_ordernow
      }.Run(this.parentWindow))
      {
                this.<UpdateLicense>g__ShowRegisterWindow|5_1();
                return;
      }
      this.OpenPurchaseUrl();
}


修改为空:
// vspd.Utils.LicenseUpdater
public void UpdateLicense()
{
}


编辑IL指令为一句
0      0000   ret    //返回空,不更新证书




E、自定义注册信息,注册名,进入 vspd.AboutWindow


public string Registered
{
      
      get
      {
                return this.<Registered>k__BackingField;      //此处改为 return "ChinaRadKe";
      }
}


删除多余IL指令,修改为:
0      0000      ldstr      "ChinaRadKe"
1      0005      ret






F、自定义注册信息,注册类型// vspd.AboutWindow
public string License
{
      
      get
      {
                return this.<License>k__BackingField;      //此处改为      return "Single License";
      }
}


删除多余IL指令,修改为 Site License 或 Single License:
0      0000      ldstr      "Single License"
1      0005      ret






G、汉化 vspd.Properties.Resources 资源,实现中文化。汉化资源可以使用 dnSpy 直接保存导出和导入。


下载 vspd.Properties.Resources, 在 dnSpy 打开的主程序 vspdpro.exe 中,删除原 vspd.Properties.Resources,
然后,导入下载的资源 vspd.Properties.Resources,汉化完成。



H、dll劫持hook
单纯破解主程序 vspdpro.exe 没有绕过服务程序的网络验证,仍然无法真正激活程序,超过14天试用期,主程序无法创建虚拟串口。


vspdpro_service.exe 的破解是巧妙的绕过验证:


激活证书在 C:\ProgramData\Electronic Team\VSPDPro\vspdpro.act


编写 WinHttp.dll 劫持 hook,劫持网络注册信息,代码很长,不再详述。
截取系统 WinHttp.dll 网络验证的返回值,将返回值修改为已经激活即可。

其中最关键的两个值key_type和errorCode,只要劫持这2个返回值即可:

key_type 授权类型
0 = Single License 单机版许可授权
1 = Limited func (license_options) 表示根据功能授权,license_options的值表示功能的限制数量
2 = Limited time(key_options=days) 表示根据时间授权,key_options的值表示授权天数。
3 = OEM


errorCode 授权状态
0 = ALREADY_ACTIVATED
DEMO_IS_OVER
NO_AVAILABLE_ACTIVATIONS
KEY_BANNED
+REACTIVATE
+STARTING

激活原理:


vspdpro.act 注册激活信息:
Act data:
{
activationDate: 2025-01-01
errorCode: ALREADY_ACTIVATED
firstActivation: 2025-01-01
hid: hid
key_type: 0
license_key_code:11111-22222-33333-44444-55555
licenseName: Single
nextActivation: 2120-01-01
product_id: 73
product_name: Virtual Serial Port Driver PRO
product_version: 9
registed_name: China Red Ke
serverDate: 2025-01-01
serverTime: 1577836800 (时间秒、时间戳)
}


证书信息字段:
hid=hid
license_key_code:11111-22222-33333-44444-55555
licenseName=Single
product_id=73
product_name=Virtual Serial Port Driver PRO
product_version=9
serverDate=2025-01-01
activationDate=2025-01-01
nextActivation=2120-01-01
firstActivation=2025-01-01
errorCode=ALREADY_ACTIVATED
key_type=0
registed_name=China Red Ke
license_options=
key_options=
serverTime=1577836800 (时间秒、时间戳)
activation_param=
hash=977e5de9c47e4dbb03e25aafa5b0f806




1、注册信息返回代码“ALREADY_ACTIVATED”字符,代表已经激活;
2、注册信息返回激活数据 Act data 合法,代表激活信息正常;
3、注册信息密钥类型 key_type,代表用户激活类型,必须与许可证密钥码 license_key_code 计算相应;
      0 表示激活用户, 代码 errorCode 为: ALREADY_ACTIVATED
      3 表示OEM用户,代码 errorCode 为: +WRONG_OEM
4、注册信息返回激活开始、结束时间、服务器时间相应合法,代表已经激活正常;
5、以上所有注册信息的哈希验证合法,代表已经激活正常;


服务程序 vspdpro_service.exe 代码中,根据激活文件内容由此产生逻辑布尔值判断:
1、act file: true 验证激活文件是有效的
2、Activation expired: false 验证激活已过期是无效的
3、External Status: ALREADY_ACTIVATED 验证外部状态是已经激活
4、IsGoodToWork: true 验证运行状态好,转向正常虚拟工作
5、IsTimeBackward: false 验证时光倒流是无效的
6、IsExpired: false 验证激活已过期是无效的
7、DaysLimit: 4 验证试用软件天数限制
8、NumberLimit: 2147483647 验证试用软件时间限制(与天数相应)
9、IsExpired: false 验证激活已过期是无效的




注册证书状态有以下内容:
1、+STARTING 表示演示结束,给出字符串“DEMO_IS_OVER”
2、+BACKWARDTIME 表示本机时间与服务器时间倒置,表示修改本机时间到过去的非法行为
3、+BADTIME 表示本机时间不正确
4、+WRONG_HID 表示工作正常,错误隐藏
5、NO_AVAILABLE_ACTIVATIONS 表示没有可用的激活
6、DEMO_IS_OVER 表示演示结束
7、ALREADY_ACTIVATED 表示已经激活
8、KEY_BANNED 表示密钥被禁止


详细定义如下:
public const string APE_OK = “0”;
public const string APE_ALREADY = “ALREADY_ACTIVATED”;
public const string APE_DEMO_IS_OVER = “DEMO_IS_OVER”;
public const string APE_MAX_HIDS = “NO_AVAILABLE_ACTIVATIONS”;
public const string APE_KEY_BANNED = “KEY_BANNED”;
public const string APE_BAD_KEY = “CANT_FIND_KEY_CODE”;
public const string APE_BAD_REQ_NOHID = “NO_REQUEST_HID”;
public const string APE_BAD_REQ_NOKEY = “NO_REQUEST_KEY_CODE”;
public const string APE_BAD_REQ_NOPID = “NO_REQUEST_PRODUCT_ID”;
public const string APE_BAD_REQ_NOVER = “NO_REQUEST_PRODUCT_VERSION”;
public const string APE_UNK_PRODUCT = “UNKNOWN_PRODUCT”;
public const string APE_UNK_VERSION = “KEY_NOT_FOR_THIS_VERSION”;
public const string APE_UNK_LICENSE = “UNKNOWN_LICENSE”;
public const string APE_SRV_ERR_SAVE = “CANT_SAVE_ACTIVATION_RECORD”;
public const string APE_SRV_ERR_UPD = “CANT_UPDATE_ACTIVATION_RECORD”;
public const string APE_STARTING = “+STARTING”;
public const string APE_WRONG_HID = “+WRONG_HID”;
public const string APE_WRONG_PRODUCT_VERSION = “+WRONG_PRODUCT_VERSION”;
public const string APE_WRONG_OEM = “+WRONG_OEM”;
public const string APE_REACTIVATE = “+REACTIVATE”;
public const string APE_GRACE = “+GRACE”;
public const string APE_GRACE_EXPIRED = “+GRACE_EXPIRED”;
public const string APE_OFFLINE = “OFFLINE”;
public const string APE_BAD_LOCALTIME = “+BADTIME”;
public const string APE_TIME_BACKWARD = “+BACKWARDTIME”;
public const string APE_CORRUPTED_DATA = “+CORRUPTED”;
public const string APE_OUTDATED = “+OUTDATED”;
public const string APE_CANNOT_SAVE_FILE = “+CANNOT_SAVE_DATA”;
public const string AKEY_demo = “demo”;
public const string AKEY_reactivate = “reactivate”;


官方证书类型:
Trial Version License
Single License (Standard/PRO version)
SDK License
Site License
Source License


下载:
本地下载   


官方原版虚拟串口驱动专业版 Eltima Virtual Serial Port Driver Pro 最新原版中文版本 V11.0.1068安装文件下载:


https://cdn.electronic.us/products/vspd/windows/download/vspd.exe

但美国或许禁止了中国IP下载,上面地址可能无法下载。

百度云下载:
链接:https://pan.baidu.com/s/1ZQW10aqsfd0LN-htArDTOA
提取码:yujq




完整文件下载,包含所有安装详细步骤、和谐过程、破解方法、汉化资源文件等


https://download.csdn.net/download/zyyujq/90680938

下载后解包密码:ChinaRedKe




学编程的闹钟 发表于 2025-4-28 11:13

谢谢分享
页: [1]
查看完整版本: 虚拟串口驱动专业版 Eltima Virtual Serial Port Driver Pro 最新中文版本 V11.0.1068 IL代码分析