拒绝硬件功能付费订阅,白嫖天价授权从我做起——以某厂商信号源为例

查看 160|回复 11
作者:月夜克星   
因为我估摸着我这涉及的东西也没几个人用得上 还是别猜这玩意是啥了 所以大家就看个乐就好 禁止模仿 所产生的后果也与本人无关
0.背景+一些废话
大家应该听过买了车加热座椅还需付费订阅的事情吧,对,这种事其实很常见,在消费电子领域之外的其他地方行之有年。基本上就是你花了很高的价格买了几乎是全功能版本的硬件,可是由于你的小钱钱出的不够,买的是乞丐版,你只能使用很少很少的一部分硬件功能,剩下的部分呢,你可能需要再出数十万来解锁。为了压缩成本,所以厂商给搞了个升级措施,美名其曰免返厂的配置升级,其实就是你花数十万去买一组KEY来把他们阉割掉的功能还回来。
当然,这种做法其实有一定的积极意义,毕竟这些东西都是死贵死贵的,基本上都要走固定资产采购流程的,这么做可以把一大笔钱拆成好几部分,对厂商方便也对出钱的机构方便,但是就是对没钱的人不是很友好,尤其是听到要花那么多钱买几十个字母的字符串,虽然知道是知识产权的费用,但是心里多少还是有点接受不了。
不说这些题外话,大家就当个故事听听好了,今天的主角是某国外知名厂商的丐中丐版本的信号源。主要工作原理是用一个windows电脑作为上位机,对下面FPGA板、时钟板、功放板组成的系统进行控制,完成信号源的各种功能(不得不说人家的维护手册是写的真的好,和某些厂商相比真是包罗万象,不过人家也很贵就是了)。
之所以要称为丐中丐版本,那是因为这个东西没有买任何一个升级选项,并且厂商的刀法和老黄比起来真是有过之而无不及。现有主要功能有且仅有单通道信号输出,信号存储个数1个,从这点上看还不如旁边几个达不溜的信号源,唯一的好处就是人家上限高啊(各种意义上的)。
但是这东西硬件支持什么呢?不用看说明书从前面板我就能看的出来,这明显是四通道嘛,存储信号个数怎么只能用一个捏?嗯,我当时阅读了手册之后还是没搞懂,于是虚心的咨询工程师之后,嗯,原来是我的马内没花够造成的,那没事了。充钱能使你变得更强,没钱咋办,凉拌咯。
一看升级选项,什么4通道,快速切换,序列器,更大的波形存储,别管用不用得到,名字就很诱人,唯一不美丽的就是那根本就不用问就知道压根不可能美丽的价格。
1.对授权实现机制的一些猜测
首先观察上位机,一看界面风格,目录下一堆clr的dll,不用想,这肯定是.Net的杰作。
观察启动信息,上面一堆
初始化固件...
初始化FPGA...
初始化License ...
的提示,打开任务管理器-服务,果不其然,XXX Licensing Service,右键停止服务,再次打开软件,果然启动不起来了。
本着对大厂商的保护措施的敬畏,我发现初始化license在初始化FPGA和Firmware后面,心里已经感觉大大的不妙,这不会是把一些必要信息和FPGA和单片机里的程序还通信了吧,单片机还好说,FPGA要逆向逻辑可就老费劲了啊,一看目录下还有好几个FPGA相关的BIN,已经想敲响退堂鼓准备撤退了。
不过在撤退之前,我还分析了激活许可证的方式进行了下简单研究,发现似乎只和XXX Licensing Service交互了一下,不过许可证的格式也太复杂了,实在搞不定,于是放弃。不过这也并非完全没有意义,这充分说明所有的关键点都在那个C#的上位机软件上。
为了防止软件里存在什么暗桩之类的,我并不打算对上位机本体进行修改,还是采用hook的方式好了,简单快捷。
2.对上位机软件的探索
首先打开Detect It Easy查了下 很好没有加壳 (多少有点疑惑了,这种大厂应该不缺钱买个DNGuard什么的)。
然后打开dnspy,额,竟然只有简单的混淆和控制流扁平化?我说你那没有用,虽然这样子凑合着也能读,不过我还是啪的一下打开了de4dot反混淆了一下,很快啊,嗯,看起来舒服多了。
2.1 初探
接下来自然是先是一个左正蹬,一下子给他入口点找到了,看了看没啥东西,转进一看没啥东西


image-20230830162630580-1693383997699.png (75.2 KB, 下载次数: 0)
下载附件
2023-8-31 13:41 上传

又一个右鞭腿,他说婷婷,我说传统功夫以点到为止,一下子给他授权初始化的地方给找到了


image-20230830162853343.png (178.04 KB, 下载次数: 0)
下载附件
2023-8-31 13:41 上传

点进去一看,一层套一层,


image-20230830163242116.png (60.91 KB, 下载次数: 0)
下载附件
2023-8-31 13:42 上传

挨个点进去读完,终于找到了重要的地方
private u SetupLicensing()
        {
            u result = new u();
            if (this.ScpiSetup == null)
            {
                return result;
            }
            string text = "******** License Service";
            string text2 = "Error when trying to connect to " + text;
            try
            {
                List list = new List();
                AcclLicenseType licType = 1;
                int connectivity = 10;
                LicenseProxy.AdditionalInitFinish.Delegate init = delegate()
                {
                    SourceWfmExplorer.Initialize(licType, connectivity, false);
                };
                LicenseProxy.AdditionalInitFinish.Delegate finish = delegate()
                {
                    SourceWfmExplorer.Finish(false);
                };
                list.Add(new LicenseProxy.AdditionalInitFinish(init, finish));
                LicenseProxy.MessageBoxShowDelegate messageBoxShow = new LicenseProxy.MessageBoxShowDelegate(this.SplashScreen.CloseShowMessageBox);
                this.licenseHandler = new Class34(this.ScpiSetup);
                if (!this.licenseHandler.Init(messageBoxShow, list))
                {
                    string text3 = text2;
                    if (this.licenseHandler.LastInitExceptionMessage != null)
                    {
                        text3 = text3 + ":" + Environment.NewLine + this.licenseHandler.LastInitExceptionMessage;
                    }
                    else
                    {
                        text3 = text3 + "." + Environment.NewLine + "See log file for details.";
                    }
                    this.SplashScreen.CloseShowMessageBox(text3, text, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                }
                GlobalLicenseHandling.Instance = this.licenseHandler;
            }
            catch (Exception ex)
            {
                this.SplashScreen.CloseShowMessageBox(text2 + "Exception: " + ExceptionHelper.Messages(ex, "\n->"), text, MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }
            return result;
        }
看到这里基本上就能明白了,只要我们把我们想要的License全部塞进this.licenseHandler,即Class34就好了。
2.2 Class34
注意到这个对象的Init方法,传进去了一些回调,所以这东西里面应该有些验证逻辑 点进去 果不其然


image-20230830163818034.png (96.53 KB, 下载次数: 0)
下载附件
2023-8-31 13:42 上传

可以看到这个类有一些没有名字的方法,应该就是关键所在了
根据名字一看Class34.method_12就是打日志的,一看就不重要
剩下4个Class34.method_10,Class34.method_2,Class34.method_0,Class34.method_1应该都是有点作用的。
大略看了下,Class34.method_1基本就是每隔10s检查下License有没有被Checkout,和它注册的名字很符合。
Class34.method_0处理各种License方面的事件,比如增加、删除、修改之类的。大部分处理逻辑就是调用下this.method_2。
至于Class34.method_0?
看图即可 读下序列号而已。


image-20230830164616393.png (85.17 KB, 下载次数: 0)
下载附件
2023-8-31 13:43 上传

这么看来,真正重要的就是Class34.method_2而已。
2.3 Class34 构造函数
先不着急,先点开前面提到的Class34看一眼


image-20230830164916113.png (262.52 KB, 下载次数: 0)
下载附件
2023-8-31 13:43 上传



image-20230830164922658.png (258.51 KB, 下载次数: 0)
下载附件
2023-8-31 13:44 上传

哦吼,还有意外收获?这下好了,压根不用找这些Feature都是啥了,现成的呀,反射下就能拿到了。
2.4 Class34.method_2
接下来就是Class34.method_2了,这可真复杂啊,没关系 挑重要的看


image-20230830165223651.png (203.89 KB, 下载次数: 0)
下载附件
2023-8-31 13:44 上传



image-20230830165230684.png (135.86 KB, 下载次数: 0)
下载附件
2023-8-31 13:45 上传



image-20230830165247359.png (132.18 KB, 下载次数: 0)
下载附件
2023-8-31 13:45 上传

数字名字的方法可真是越来越多了。。。
2.5Class34.method_9
从上到下,先来看Class34.method_9好了


image-20230830165347983.png (169.46 KB, 下载次数: 0)
下载附件
2023-8-31 13:45 上传

总结下基本就是读一下CalTable,和模块支持的所有feature列表 对比列表里每一项是不是在CalTable里(看一下是不是硬件支持的应该是)是的话就加入到CaltableModuleFeatures里 只不过根据是不是硬件里的caltable是否是 EnableState去设置不一样的EFlags 别问我这缩写都是啥 俺不是做仪器的俺也不知道 你就看好不好使就完事了


image-20230830165545585.png (46.3 KB, 下载次数: 0)
下载附件
2023-8-31 13:46 上传

这个是InterestingValidFeaturesAndSetsInAlm


image-20230830165613221.png (127.4 KB, 下载次数: 0)
下载附件
2023-8-31 13:46 上传

**这个ALM应该指的是******* License Manager 这个进一步过滤出ALM里添加过的并且还是interesting的feature**
这个是InterestingFeaturesAndSets


image-20230830165722896.png (197.44 KB, 下载次数: 0)
下载附件
2023-8-31 13:46 上传

这个是把CalTable中的已经被enable的feature从中去掉,保留剩下的并且把所有的支持的FeatureSet也加入这个list 并返回 所以是interesting 意思是剩余的可以被激活的feature
看到这里终于大概明白了些。。。
2.6 Class34.method_5
终于可以看Class34.method_5 了
不放图出来了 基本意思是检查License的前置条件 比如你要用4通道,那你得先买两通道吧,对它就是这么过滤一下 防止你跳关
不过里面最重要的部分是检查了和现有的CalTable的变化 然后给打上各种各样的Flag
比如移除 添加 是否需要拷贝到机器硬件模块里等等
比如你这次开机花了10个W买了个尊贵的功能,这时候CalTable就该变化了 于是打上 CopyToModule的FLAG
然后你就能用上了
2.7 Class34.method_6
下面就是Class34.method_6 了
各种各样的验证 从硬件读一下 CalTable与现有比较 确定下你有没有搞坏事情 然后就是关键的写入CalTable了


image-20230830170632590.png (99.65 KB, 下载次数: 0)
下载附件
2023-8-31 13:55 上传

从调用关系可以看出 最上面那个有一个bool参数 参数名叫burn,应该是是否永久写入吧 我不敢永久改变 所以还是 false的好
毕竟这也是我用hook方法的原因
2.8 Class34.method_杂
下面就是Class34.method_7和Class34.method_8 了 主要是检查你花的钱有没有到期 到期了赶快给你取消 别让你占便宜
和咱们没关系 忽略
3. 最终成果
最终是打算使用HarmonyLib实现Hook 使用app.manifest 来实现代码的注入 这样子方便快捷又便于隐藏 状态的切换也很简单 不需要对原本程序本身进行patch 只需要保存两个版本的xxx.exe.config即可实现注入和不注入的切换
首先是利用AppDomain 可以注册AssemblyLoad事件的处理函数的机制 实现自定义代码的注入
关键代码如下 (没有软件工程方面的背景 代码写的比较丑 见笑了)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Reflection;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Security;
using System.Runtime.Remoting;
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using HookInterface;
namespace AppDomainInjector
{
    [SecurityCritical]
    [ComVisible(true)]
    [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
    public class MyAppDomainMgr : AppDomainManager
    {
        private static string HookAssemblyFile = "";
        private Assembly HookAssembly = null;
        private static IHookEventHandlers hookEventHandlers = null;
        private static readonly AssemblyLoadEventHandler assemblyLoadEventHandler = OnAssemblyLoadInternal;
        //private static readonly ResolveEventHandler AssemblyResolveEventHandler = null;
        //private static readonly UnhandledExceptionEventHandler UnknownExceptionHandler = null;
        public MyAppDomainMgr()
        {
            //Console.WriteLine("hhhhh");
            //WinConsole.Initialize(true);
            //Console.WriteLine("app domain injector start!");
            //Debugger.Launch();
            FindHookAssemblyFile();
            if (string.IsNullOrEmpty(HookAssemblyFile))
            {
                //Console.WriteLine("Place a dll file named _Hook.dll !!");
                throw new Exception("Can't find Any Hook Dll File !");
            }
            HookAssembly = Assembly.LoadFile(HookAssemblyFile);
            if(HookAssembly != null)
            {
                //Console.WriteLine("Success Load "+ HookAssembly.FullName);
            }
            var types = HookAssembly.GetExportedTypes();
            foreach (var type in types)
            {
                var IT = type.GetInterface("IHookEventHandlers");
                if (IT!=null) {
                    //Console.WriteLine("Found!!  " + type.AssemblyQualifiedName);
                    try
                    {
                       hookEventHandlers = HookAssembly.CreateInstance(type.FullName) as IHookEventHandlers;
                    }
                    catch(Exception ex)
                    {
                        //Console.WriteLine(ex.Message);
                    }
                }
            }
            if(hookEventHandlers == null)
            {
                //Console.WriteLine("Error Find The Class IMPLEMRNTS IHookEventHandlers");
            }
            AppDomain.CurrentDomain.AssemblyLoad += assemblyLoadEventHandler;
            //AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolveEventHandler;
            //AppDomain.CurrentDomain.UnhandledException += UnknownExceptionHandler;
        }
        private static void OnAssemblyLoadInternal(object sender, AssemblyLoadEventArgs args)
        {
            //Console.Write("[OnAssemblyLoad] ");
            //Console.WriteLine(args.LoadedAssembly.FullName);
            AppDomain.CurrentDomain.AssemblyLoad -= assemblyLoadEventHandler;
            try
            {
                hookEventHandlers.OnAssemblyLoad(sender, args);
            }catch (Exception ex)
            {
               // Console.WriteLine(ex.Message);
                //Console.WriteLine(ex.StackTrace);
            }
            AppDomain.CurrentDomain.AssemblyLoad += assemblyLoadEventHandler;
        }
        private void FindHookAssemblyFile()
        {
            var path = AppDomain.CurrentDomain.BaseDirectory;
            var fns = Directory.GetFiles(path, "*_Hook.dll");
            if(fns.Length != 1)
            {
                //Console.WriteLine("Error not only one dll file named with _Hook.dll postfix!");
                throw new Exception("Error only than one dll file named with _Hook.dll postfix!");
            }
            HookAssemblyFile = fns[0];
            //Console.WriteLine("Success "+ HookAssemblyFile.ToString());
        }
    }
注意由于安全原因 编译出来的代码需要强签名 这个感觉很常见 搜索一下就找得到解决方案
然后修改xxx.exe.config文件的方法如下:
      
      
注意按照你自己签名修改对应部分,然后把这部分加入runtime段就好。
然后这玩意就会自动加载同目录下_Hook.dll为后缀的其他自动义钩子了。
虽说是为了可拓展,其实到现在都一次没拓展过:D。 另外,之所以这么干
看起来是脱裤子放屁,但是这样子可以避免每次都要强签名乱七八糟的 麻烦 可能是我没配置好吧
这样子搞个壳至少可以调试下面代码时候不用搞来搞去了
主要的关键HOOK代码如下:
        [HarmonyPatch("InstalledFeatureDefinitions")]
        [HarmonyPostfix]
        public static IEnumerable HookInstalledFeatureDefinitionsPostfix(IEnumerable aaaa)
        {
            FileLog.Log("Invoked " + new StackTrace(new StackFrame(true))?.GetFrame(0)?.GetMethod()?.Name);
            var a = new *******License(
                "004", "1.000", "2099-12-31", 99, DateTime.MinValue, true, "suihduihdiuhd", "sihdsdhudhidu",
                "suihgsuighsuigs", "JustForTest", null, "suiwhgsuiweh", "woisjhiowsjhw", "wsoihjswi", "iow",
                null, false, false, true, false, true);
            var b = new *******License(
                "16G", "1.000", "2099-12-31", 99, DateTime.MinValue, true, "suihduihdiuhd", "sihdsdhudhidu",
                "suihgsuighsuigs", "JustForTest", null, "suiwhgsuiweh", "woisjhiowsjhw", "wsoihjswi", "iow",
                null, false, false, true, false, true);
            var c = new *******License(
                "FSW", "1.000", "2099-12-31", 99, DateTime.MinValue, true, "suihduihdiuhd", "sihdsdhudhidu",
                "suihgsuighsuigs", "JustForTest", null, "suiwhgsuiweh", "woisjhiowsjhw", "wsoihjswi", "iow",
                null, false, false, true, false, true);
            var d = new *******License(
                "SEQ", "1.000", "2099-12-31", 99, DateTime.MinValue, true, "suihduihdiuhd", "sihdsdhudhidu",
                "suihgsuighsuigs", "JustForTest", null, "suiwhgsuiweh", "woisjhiowsjhw", "wsoihjswi", "iow",
                null, false, false, true, false, true);
            yield return a;
            yield return b;
            yield return c;
            yield return d;
}
别的就是Hook了一些关于CalTable读写的东西 方便调试用
最终结果 放一段调试时候的日志吧 就不放界面了
Invoked ReadCaltable!!!
==================Caltable==================
ALL : 1
R14 : 0
R12 : 0
001 : 0
002 : 0
004 : 1
16G : 1
SEQ : 1
FSW : 1
RI09 : 0
RI10 : 0
RI11 : 0
RI12 : 0
RI13 : 0
RI14 : 0
RI15 : 0
RI16 : 0
RI17 : 0
RI18 : 0
RI19 : 0
RI20 : 0
RI21 : 0
RI22 : 0
RI23 : 0
========Caltable=========END==============
可以看到所有功能全开了 其他的一些没名字可能是保留用的 不知道作用还是不乱动比较好。
本文全部内容仅供学习交流使用 不提供任何下载 禁止用于其他任何用途 所产生一切后果均与本人无关
========================================全文完===================================================
不行,最后还是要吐槽一下52的这个MarkDown编辑器,不支持压缩包上传图片就算了,没几张图点点也还行。
这个内容一多,每行字写得一长,粘贴进那个MarkDown发帖拓展工具,就会导致右边预览的部分布局直接超出屏幕,顺带着右下角的提交按钮你就点不到了。
这是正常的还是我不会用这玩意啊??
我寻思左下角这个预览按钮我也没有点啊,怎么就直接渲染了呢?怎么会是呢?
附临时解决办法 粘贴进去之后打开控制台 执行 $("e_zxsq_editor_submit").click()
这行js能帮你点一下那个按钮 毕竟按钮只是被挤出屏幕了 不是消失了

下载次数, 下载附件

saonan   

好家伙  你看看   这玩的是技术吗   这完全是 心理战术啊
fitianw   

国外硬件都是这么授权的。。。我去的学校实验室,买了促销的板子a,和b,促销的plc c和d,结果分别是四家不同公司的,买了四个软件的授权。
软件大版本升级还要补升级费,所以实验室到现在还是用的类似于98的那种复古UI的软件
cy465728759   

我来了,能不能直接给个做好的。
火星大能猫   

看不懂,不过貌似对现在电车车主有帮助
3yu3   

哪家??福特吗??
hipojie   

HOOK方法有新思路?
月夜克星
OP
  

老哥 看看这个能不能搞 https://www.52pojie.cn/thread-1823261-1-1.html
yiting8   


hipojie 发表于 2023-8-31 15:02
老哥 看看这个能不能搞 https://www.52pojie.cn/thread-1823261-1-1.html

看起来挺有意思的工具,头次听说,收藏了
Kls673M   

得先有车
您需要登录后才可以回帖 登录 | 立即注册

返回顶部