lvyiwuhen 发表于 2019-11-18 14:51

去除友善串口调试助手注册提示弹窗

本帖最后由 lvyiwuhen 于 2022-11-5 14:25 编辑

(文末有惊喜)工作原因,经常使用串口调试助手。虽然网上完全免费的串口调试助手很多,但是用习惯了友善,不想换其他的工具。
这个软件30天试用期过后,会不定时弹出注册提示窗口,有些烦人。于是想着去除掉。
因自己不会分析exe进行脱壳破解一类的,只有用偏门的思路,快速解决问题,忘坛友们不要嘲笑:lol
以下是我的去窗口思路。
https://static.52pojie.cn/static/image/hrline/1.gif

使用工具:VisualStudio2013
本次测试的友善串口调试助手版本:3.7.1
https://static.52pojie.cn/static/image/hrline/1.gif


一、助手正常界面和注册提示界面


二、针对上述注册提示窗口(模态窗口,本窗口不关闭,主窗口无法使用),我首先想到的方法是:使用系统的Findwindow和FindWindowEx函数,查找窗口,并调用SendMessage关闭。
按上述方法进行分析,
1.首先查看Findwindow和FindWindowEx函数的参数

/// <summary>
/// 查找窗口
/// </summary>
/// <param name="lpClassName">窗口类名称</param>
/// <param name="lpWindowName">窗口标题名称</param>
/// <returns></returns>

private extern static IntPtr FindWindow(string lpClassName,string lpWindowName);
/// <summary>
/// 查找窗口
/// </summary>
/// <param name="hwndParent">父窗体句柄</param>
/// <param name="hwndChildAfter">子窗体句柄</param>
/// <param name="strClass">窗口类名</param>
/// <param name="strWindow">窗口标题</param>
/// <returns></returns>

private extern static IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter,string strClass, string strWindow);

可以看出,调用这两个函数需要知道窗口的类名或者窗口标题名称。另外从助手界面可以看出两个窗口的名称都是一样的,看看两个窗口的类名是否一样,不一样的话,就可以直接查到这个窗口。使用VS自带的spy++查找窗口的句柄和类名,上图a.主窗口句柄

b.注册提示窗口句柄


从上面两个窗口可以得到的数据:
主窗口
句柄:000A0FAE(10进制:659374)
标题:友善串口调试助手
类:Qt5QWindowIcon
注册提示窗口
句柄:001E0FA2(10进制:1970082)
标题:友善串口调试助手
类:Qt5QWindowIcon

发现两个窗口的类名称也都一样,没办法直接按名称或类名关闭窗体了。
观察这两个窗口句柄,发现注册提示窗口的句柄值要大于主窗口的句柄值,仔细想想,除了第一次打开程序先弹出的注册提示窗口外,其它提示窗口的都是在主窗口出现之后出现的,后续提示窗口的句柄肯定大于主窗口的句柄,所以可以通过判断句柄值大小来确定哪个是我们的目标窗口。
2.实际上在提示窗口出现的情况下,多次调用FindWindow函数,每次获得的窗口句柄始终是弹出窗口的句柄,得不到主窗口的句柄;将FindWindow得到的弹出窗口句柄传入FindWindowEx的主窗体句柄参数位置可实现查找到主窗体。比较两个窗体的句柄大小,然后关闭弹出窗口句柄。

private void StartDetect2()
       {
         string winname = "友善串口调试助手";
         string winclsname = "Qt5QWindowIcon";
         _ThreadDetect = new Thread(() =>
         {
                while (true)
                {
                  Application.DoEvents();
                  Thread.Sleep(1000);
                  IntPtr hwnd1 =FindWindow(winclsname, winname); //通过窗体类名称和窗体名称查找句柄,精确过滤。如果只写窗口名称,也会查到文件夹名称为友善串口调试助手的窗口。影响后面的判断。
                  if(hwnd1!=IntPtr.Zero)
                  {
                        IntPtr hwnd2 =FindWindowEx(hwnd1, IntPtr.Zero, winclsname, winname);
                        if(hwnd2!=IntPtr.Zero)
                        {
                            if (hwnd1.ToInt64()> hwnd2.ToInt64())
                            {
                               SendMessage(hwnd1, 16, 0, 0);//关闭窗口,通过发送消息的方式
                            }
                            else
                            {
                               SendMessage(hwnd2, 16, 0, 0);//关闭窗口,通过发送消息的方式
                            }
                        }
                  }
                }
         });
         _ThreadDetect.Start();
       }

3.上述方法试验后发现:只能在打开主窗口后,弹出注册提示窗之前,运行该方法才会有效,并且只能关闭一次弹出窗口。
分析原因,因为,弹出窗口出现后,FindWindow获取到的就是弹窗的句柄,该句柄值大于主窗口句柄,导致FindWindowEx查不到主窗口句柄。
三、通过上面的分析发现上面的思路达不到目的,于是考虑使用:查询桌面所有窗口句柄,然后将窗口名称和类名称都符合要求的窗口添加进一个List,然后再判断句柄大小,关闭大句柄的窗口。
直接上核心代码:

usingSystem.Runtime.InteropServices;
usingSystem.Threading;
usingSystem.Diagnostics;
usingSystem.IO;

///<summary>
      /// 发送系统消息(可用来发送关闭窗口命令)
      /// </summary>
      /// <paramname="hWnd"></param>
      /// <paramname="msg"></param>
      /// <paramname="wParam"></param>
      /// <paramname="lParam"></param>
      /// <returns></returns>
      
      private static extern intSendMessage(IntPtr hWnd, int msg, uint wParam, uint lParam);

      /// <summary>
      /// //用来遍历所有窗口
      /// </summary>
      /// <paramname="lpEnumFunc"></param>
      /// <paramname="lParam"></param>
      /// <returns></returns>
      
      private static extern boolEnumWindows(WNDENUMPROC lpEnumFunc, int lParam);

      /// <summary>
      /// 获取窗口Text
      /// </summary>
      /// <paramname="hWnd"></param>
      /// <paramname="lpString"></param>
      /// <paramname="nMaxCount"></param>
      /// <returns></returns>
      
      private static extern intGetWindowTextW(IntPtr hWnd, StringBuilderlpString, int nMaxCount);

      /// <summary>
      /// 获取窗口类名
      /// </summary>
      /// <paramname="hWnd"></param>
      /// <paramname="lpString"></param>
      /// <paramname="nMaxCount"></param>
      /// <returns></returns>
      
      private static extern intGetClassNameW(IntPtr hWnd, StringBuilderlpString, int nMaxCount);

      /// <summary>
      /// 声明一个委托函数用于 Win32 API - EnumWindows 的回调函数:
      /// </summary>
      /// <paramname="hWnd"></param>
      /// <paramname="lParam"></param>
      /// <returns></returns>
      private delegate bool WNDENUMPROC(IntPtr hWnd,int lParam);//IntPtr hWnd用int也可以


/// <summary>
      /// 自定义一个结构体,用来保存句柄信息
      /// </summary>
      public struct WindowInfo
      {
            public IntPtr hWnd;
            public string szWindowName;
            public string szClassName;
      }

/// <summary>
      /// 获取所有标题相同的窗体句柄及窗体类名
      /// </summary>
      /// <paramname="windowText">窗体标题名称</param>
      /// <paramname="clsText">窗体类名称</param>
      /// <returns></returns>
      public List<WindowInfo>GetAllWindows(string windowText,string clsText)
      {
            //用来保存窗口对象 列表
            List<WindowInfo> wndList =new List<WindowInfo>();

            //enum all desktop windows
            EnumWindows(delegate(IntPtr hWnd, intlParam)
            {
                WindowInfo wnd = newWindowInfo();
                StringBuilder sb = newStringBuilder(256);

                //get window name
                GetWindowTextW(hWnd, sb,sb.Capacity);
                wnd.szWindowName =sb.ToString();
                //get window class
                GetClassNameW(hWnd, sb,sb.Capacity);
                wnd.szClassName =sb.ToString();

                if((wnd.szWindowName==windowText)&& (wnd.szClassName==clsText))
                {
                  //get hwnd
                  wnd.hWnd = hWnd;

                  //add it into list
                  wndList.Add(wnd);
                }

                return true;
            }, 0);
            return wndList;
      }

Thread_ThreadDetect;//创建一个线程
/// <summary>
      /// 开始检测
      /// </summary>
      private void StartDetect()
      {
            string winname = "友善串口调试助手";
            string winclsname ="Qt5QWindowIcon";
            _ThreadDetect = new Thread(() =>
            {
                while (true)
                {
                  Application.DoEvents();
                  Thread.Sleep(1000);
                  List<WindowInfo>winInfo = GetAllWindows(winname, winclsname);
                  if (winInfo.Count >= 2)
                  {
                        long h1 =winInfo.hWnd.ToInt64();
                        long h2 =winInfo.hWnd.ToInt64();
                        if (h1 > h2)
                        {
                           Console.WriteLine("找到窗口,句柄为:"+ h1.ToString());
                           SendMessage(winInfo.hWnd, 16, 0, 0);//关闭窗口,通过发送消息的方式
                        }
                        else
                        {
                           Console.WriteLine("找到窗口,句柄为:"+ h2.ToString());
                           SendMessage(winInfo.hWnd, 16, 0, 0);//关闭窗口,通过发送消息的方式
                        }
                  }
                }
            });
_ThreadDetect.IsBackground = true;
            _ThreadDetect.Start();
      }

private voidfrmCloseOtherForm_Load(object sender, EventArgs e)
      {
            //将本程序exe放在友善串口助手根目录,打开本程序自动打开友善串口助手
            if(File.Exists("SerialPortUtility.exe"))
            {
                Process.Start("SerialPortUtility.exe");
            }
            StartDetect();
            this.ShowInTaskbar = false;
      }



https://static.52pojie.cn/static/image/hrline/1.gif

友善串口助手官方下载链接:http://www.alithon.com/downloads
(安装后,注册提示窗要在30天试用期过后才会显示。)
自己的Demo下载链接:
链接:https://pan.baidu.com/s/11px3ASYy_4R7fxAf4Qlw9Q提取码:rqid
密码:论坛默认


*****************************************************************************************************************************************************
2021-02-08更新
今天使用时发现拦截不了弹出窗口,又分析了一下窗口的样式不同,获取窗口样式直接可以区分,测试了一下,有效。
图一:主窗口样式

图二:弹出窗口样式


主代码修改如下:

private const int GWL_STYLE = (-16) ;            //窗口样式/// <summary>
      /// 获得所属句柄窗体的样式函数
      /// </summary>
      /// <param name="hWnd"></param>
      /// <param name="nIndex"></param>
      /// <returns></returns>
      
      static extern int GetWindowLong(IntPtr hWnd, int nIndex);//获得所属句柄窗体的样式函数

/// <summary>
/// 开始检测
/// </summary>
private void StartDetect()
{
    string winname = "友善串口调试助手";
    //string winclsname = "Qt5QWindowIcon";
    _ThreadDetect = new Thread(() =>
    {
      while (true)
      {
            Application.DoEvents();
            Thread.Sleep(500);
            List<WindowInfo> winInfo = GetAllWindows2(winname, "96CC0000");
            if (winInfo.Count > 0) SendMessage(winInfo.hWnd, 16, 0, 0);//关闭窗口,通过发送消息的方式
         
      }
    });
    _ThreadDetect.IsBackground = true;
    _ThreadDetect.Start();
}

public List<WindowInfo> GetAllWindows2(string windowText, string clsText)
      {
            //用来保存窗口对象 列表
            List<WindowInfo> wndList = new List<WindowInfo>();

            //enum all desktop windows
            EnumWindows(delegate(IntPtr hWnd, int lParam)
            {
                WindowInfo wnd = new WindowInfo();
                StringBuilder sb = new StringBuilder(256);

                //get window name
                GetWindowTextW(hWnd, sb, sb.Capacity);
                wnd.szWindowName = sb.ToString();               
                Int32 windowStyle = GetWindowLong(hWnd, -16);
                int value = int.Parse(clsText, NumberStyles.AllowHexSpecifier);

                if ((wnd.szWindowName == windowText) && (windowStyle == value))
                {
                  //get hwnd
                  wnd.hWnd = hWnd;
                  wndList.Add(wnd);
                }

                return true;
            }, 0);
            return wndList;
      }





https://static.52pojie.cn/static/image/hrline/1.gif

C#开源串口调试助手,中英文支持,完全免费,功能多样,欢迎使用。
串口开发参考完美源码
地址:https://gitee.com/LvYiWuHen/byserial


lvyiwuhen 发表于 2019-11-18 20:06

y294945022 发表于 2019-11-18 17:04
谢谢楼主分享。只是不太清楚这款串口调试工具的方便之处或较于其它的串口工具有什么优势吗?

刚想了想比其他串口工具好的地方,比如通讯比较稳定,支持TCP客户端服务端,附带的工具箱等。可能最主要的还是使用习惯吧,习惯了这个操作界面,换其他的不适应。

lvyiwuhen 发表于 2019-11-18 20:11

现代味儿 发表于 2019-11-18 16:27
获取窗口大小 窗口内组件名 均可呀 并非一条思路

是的,只是自己水平比较低,有想到获取窗口大小,窗口模态及窗口内按钮的信息,却不知道用哪些函数,最终只有想到这个对我来说比较直观的思路。
破解,经验和技术积累直接影响分析破解的思路

晓雨的安安呀 发表于 2019-11-18 15:09

感谢楼主分享~

墨涵 发表于 2019-11-18 15:15

谢谢LZ分享

往復不息 发表于 2019-11-18 15:15

虽然我不会,但是你很棒,哈哈

yulinsoft 发表于 2019-11-18 15:36

redstking 发表于 2019-11-18 15:36

现在还看不懂,一点一点学习,感谢楼主分享思路

jianfeii 发表于 2019-11-18 15:43

我觉得友善之臂不好用啊,还是sccom好用,这款有什么特殊功能么?

Dear_tianjz 发表于 2019-11-18 15:58

这个还是蛮有用的 谢谢了

lvyiwuhen 发表于 2019-11-18 16:03

yulinsoft 发表于 2019-11-18 15:36
用GetWindowLong获取窗口样式,这个值两个窗口是不同的,而且是固定的,可以更方便操作。

多谢提示。我当时也注意到这个不同点,但是不知道用什么函数获取,学习了

jasonshake 发表于 2019-11-18 16:08

额,网上好像很多版本就直接没弹窗了
页: [1] 2 3 4 5
查看完整版本: 去除友善串口调试助手注册提示弹窗