关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)

几年前,公司买了台国产贴片机,里面的主程序是QT编写,运行在WINDOW XP系统上。主程序打开的界面,如图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第1张图片

我来简单介绍下程序界面,各位读者不需要搞明白功能,只要知道大体的流程即可。

分析主界面:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第2张图片

一、左边的列表:

贴片生产文件,里面包括了贴片时元器件的坐标、飞达安装的元器件类型、Mark定位点等信息的贴片机工艺文件(文件的格式为CSV),使用和编辑工艺文件时,需先点击选中。

二、右边的三个按钮:

  1. 用打开自带的WPS,以便直接编辑CSV工艺文件,但是因为CSV文件格式比较复杂,一般不使用此功能;
  2. 中间的编辑是在程序中详细设定CSV
  3. 最后的加工按钮,就是进入贴片加工工艺,也是我们今天需要分析的重点项目。 

分析加工界面:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第3张图片

注意: 因为逆向不能影响生产,我是在虚拟机下运行的主程序,所以下面的6个摄像头控件是不工作的,实际是显示了6个吸嘴取料的放大照片。同时因为我运行的是贴片机的默认工艺文件,所以左边的列表也是不显示内容的

一、左侧部分:

  1. 最左侧的列表,显示了MARK点信息(MARK点是PCB板上的一个标记,因为每次进板时位置的不同,所以需要MARK点,来定位每块PCB板的实际坐标,方便程序换算实际贴片坐标用的)。
  2. 中间的列表显示的贴片元器件的详细参数列表。

二、右侧部分:

  1.  加工日志,显示了PCB加工的结果信息。
  2. 功能按钮区域:
    1. 启动:开始贴片
    2. 单步(Enter):贴片每步细化,按此按钮一次即操作一步,比如:进板-->识别板边-->识别Mark点-->吸嘴取料-->贴装-->吸嘴再次取料-->再次贴装-->吸嘴N次取料-->N次贴装-->结束贴装
    3. 停止:在贴装时的暂停操作
    4. 配置:这个我们在下面会详细说说,里面包括了我们需要修改的同取功能
    5. 退出加工:顾名思义退出贴片
    6. PCB退板:把贴好的或者不贴的PCB板退出贴片机

三、下部:

  1. 六个贴装头取料时的照片(飞行相机拍摄)
  2. MARK点图片(MARK相机识别,在图1,复用贴装头1)

因为这台贴片机是多个贴装头的,所以取料时可以并排同时取多个料栈的元器件(需要满足连续的料栈才能同取),但是让人费解的是,这个主程序的贴片配置中(功能按钮中的"配置"按钮),关于同取的设置,每次退出加工程序都会重置,如果没有设置,就会变成没有同取,如下图所示:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第4张图片

逆向目标: 

        所以我们需要实现的目的已经明确:"取料支持同取"自动勾选(不用每次去按"配置"按钮,自动设置同取操作)

逆向思路:

在这里我想分享我逆向程序的一些经验给大家:

  1. 针对一个未知源代码程序时,我们需要大体知道一个程序的界面流程,可以在心里构建出一个大概的分析目标,不能没有目的。
  2. 特别是定位关键的代码时,我们至少需要大体知道代码所在的区域在哪里。
  3. 使用IDA找到函数调用堆栈链。
  4. 还有这个关键代码一定是修改了某些变量,我们只要定位到这个变量。
  5. 使用我们的二分算法,在这个区域内的中间函数下断点,观察定位到的变量是否改变成理想值。
    1. 如变量改变,则在前半段代码区域使用相同的方法,不停缩小范围,最终定位到关键函数。
    2. 如变量不改变,则在后半段区域再次使用二分法不停缩小范围,最终定位到关键函数。
  6. 在关键函数内,通过同样的二分法,定位到关键代码跳转即可。

接下来,我就为大家演示下分析流程。

逆向分析流程:

根据上面的逆向思路,我们已经有了第一条的分析目标--"取料支持同取"自动勾选;所以我们看看第二条的代码所在区域,根据我们之前分析的主界面和加工界面相关信息,我们知道,点击主界面的"加工"按钮会进入加工界面,再点击"配置"按钮可以设置"取料支持同取"

流程如下:"加工"-->"配置"-->"取料支持同取"

因为我们需要勾选"取料支持同取"(下面简称:"同取"),所以不免要从程序初始化CheckBox控件代码入手,我们暂时还不知道QT里是怎么初始化CheckBox控件(因为CheckBox控件有选中和未选中两种状态,所以这个初始化的过程包括:修改并保存一个类似布尔值的公共变量,然后通过读取这个公共变量的值,来修改CheckBox的属性。PS:因为代码设计的关系,点击退出加工后,这个布尔值会重置),我们现在还不知道这个变量的位置,但是可以确定的是,初始化这个控件时,一定会初始化"同取"字符串,如果我们能定位到初始化"同取"的代码处,我们就可以通过IDA的Xref grap to确定初始化CheckBox的函数调用链。

如果大家还不是太明白,我们实际来操作一下吧!

打开我们的IDA8.3,加载贴片机主程序,按Shift+F5打开字符串窗口(怎么使用IDA8.3搜索中文字符串的方法请看以下链接。)

最新IDA8.3安装后需要做的一些完善工作(包括IDAPython报错、ChatGPT的模块安装、中文字符串的显示,各种问题解决方法合集)icon-default.png?t=N7T8https://blog.csdn.net/donglxd/article/details/135243027如下图所示,在String窗口中,我们搜索下"取料支持同取"字符串,可以看到有一个结果,点击后,我们很方便的定位到调用它的代码处。

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第5张图片

点击上面的搜索到的字符串来到字符串的定义位置,如下图所示: 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第6张图片

点击上图中字符串后面的引用代码地址,我们来到了如下图的地址处: 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第7张图片

选中如上图中的引用代码位置,然后按一下空格,切换到如下图的方框格模式,方便我们找到函数头,我们只要点击下图中红框处的函数规模总体预览图的最上面,就可以快速定位到函数头部。 

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第8张图片

如下图:来到函数头部后,我们右击第一行代码,在弹出的菜单中选择Xrefs graph to来显示函数的调用链 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第9张图片

 如下图就是刚刚引用"同取"字符串之前运行的所有调用函数链。关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第10张图片

 知道了函数调用链,我们接下去分析下,因为我们要找到设置CheckBox的公共变量,而设置CheckBox的操作,QT一定有相应的API函数,我们打开IDA搜索下导入表,如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第11张图片

 至于那个API,查询微软的Copilot AI后,给出了下面的链接(QT系统学习系列:1.1 QAbstractButton(按钮抽象基类)),这个链接中说明了AbstractButton是checkbox的基类,CheckBox有两个函数setChecked和isChecked,分别是设置CheckBox和读取CheckBox状态。

然后我们再次使用Copilot查询setChecked函数的名称修饰,如下图:

所以,我们IDA查询到的第一个函数"_ZN15QABstractButton10setCheckedEb"就是setChecked,同样的方法查询到"_ZNK15QAbstractButton9isCheckedEv" 就是isChecked,既然知道了setChecked这个API函数,我们查找下引用他的地址有哪些,方法如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第12张图片

 点击上图中的setChecked名称修饰,定位到下图中的函数输入表位置,右击后打开函数调用链图关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第13张图片

 如下图所示,setChecked的调用链,咋一看很复杂是不是?是不是想放弃不接着看了,其实不用想的太复杂,有时答案就在眼前,只要你耐心查找。请接着读下去。

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第14张图片

你们还记得,我们刚刚也查找过一个函数调用链吗? 对了,就是调用同取函数那个调用链,我们把两张图放一起对比下,如下图所示: 

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第15张图片

 我用红笔圈出的两张图的相同之处,显然这个函数调用链(sub_4A2500-->sub_4A23E0-->sub_4362F0-->sub_491660-->sub_4CBB40)中的最后一链sub_491660-->sub_4CBB40中包含了sub_491230-->"_ZN15QABstractButton10setCheckedEb"(setChecked),所以我们只需找到sub_491230中的setChecked,以方便之后,我们定位到之前提到的公共变量位置。

我们先用IDA定位到sub_491230函数看看,因为里面有setChecked函数调用,具体的分析请看下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第16张图片

因为程序是X86的,所以我们用X32DBG调试下,如下图: 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第17张图片

可以看到内存地址74D0C8就是setChecked的公共变量参数(根据call esi得知,esp就是第一参数,而mov dword ptr ss:[esp],eax这句设置了esp,所以eax的赋值语句就是这个第一参数,也就是公共变量),打开主程序后运行启动,打开加工窗口后这个变量会一直存在,我们可以尝试设置下复选框,看看这个公共变量会改变吗?如下图:

 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第18张图片

可以看到公共变量74D0C8变成了00 00 01 01的格式,因为内存中是从高到低读取,所以我们需要的是从右往左数第二个字节的01 。关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第19张图片

 既然我们知道了公共变量,让我们再来分析下程序的流程,根据之前的程序流程分析,在主程序页面点击加工后,公共变量就建立了(这点从退出加工页面就会重置"同取"设置就可以判断出来),所以我们从主程序页面的"启动"这个字符串"开刀",Strings搜索下"启动",如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第20张图片

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第21张图片

 下图是函数头部:

 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第22张图片

右击这个sub_433830,点击Xrefs graph to,查看函数调用链: 

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第23张图片函数sub_433830的调用链如下图右半部分,可以看到sub_433830的调用链在setChecked调用链的前面,和我们分析的一样:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第24张图片

 如上图,我们需要判断设定公共变量74D0C8是否在蓝色部分的这些函数里? 所以我们先把蓝色部分的这些函数头部都下断点,使用之前文章中提到的二分法,逐步缩小区域即可。我们在x32dbg中使用bp命令快速下断点,如bp 4352C0,下断之后的效果如下:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第25张图片

然后重新运行程序,把下面的内存也设置到公共变量74D0C8的地址观察变化。程序启动后,点击主界面的"启动"按钮,遗憾的是程序直接运行到了加工界面,断点并没有断下,而是需要点击加工界面里的配置才会断下,观察公共变量74D0C8发现,在进入加工界面后,就已经被设置,所以我们可以尝试sub_433830函数的另一边调用链,如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第26张图片

我为了调试时,观察方便,把红框中下断的每个函数都注释了一个简称,如sub1、sub2这样的,方便辨认,并在X32dbg中也对应的注释了函数简称,如下图:

然后,我们用x32dbg重新运行程序,我们一边观察公共变量74D0C8的变化,一边在中断时按运行按钮,跳过不需要的断点,如果x32dbg不中断时,我们再切会贴片机程序正常操作下,如点击项目名、点击启动等,经过几次中断后,我发现公共变量74D0C8变化成我们需要的00 00 00 01了,如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第27张图片

如上图,因为中断在sub5函数头部,所以设置公共变量的代码一定在sub4函数,然后我们可以使用,如下图的二分法,逐步缩小查找范围,定位到关键代码(记住每次都要重启程序调试,我们可以按空格键把不需要的断点关掉,加快调试速度。还有别忘了重启后每次都要在内存1中定位到公共变量74D0C8)。

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第28张图片

通过上面的方法,我们很快定位到了关键函数,如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第29张图片

如上图,可以看到运行过注释的这句关键点,下面的公共变量74D0C8变成了00 00 00 01,显然设置代码在这个函数(关键点)中,在这个关键函数上下断。

把不需要的断点取消,重新运行程序,按F7进这个函数看看,如图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第30张图片

如上图,进关键函数后是一个大跳转,此时单步下去,并注意下面的公共变量74D0C8的值。

我们跟进这个大跳转,如图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第31张图片

如上图,我们来到了设置公共变量74D0C8的关键函数处,粗略看了下都是mov的赋值操作,看来地方来对了。这个函数不长,我们一行行单步观察公共变量的值变化就好。 

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第32张图片

如上图可知,我们运行了上面4行mov的赋值代码后,74D0C8变量成我们需要的值,但是这个00 00 00 01是不设置同取的变量。不知道大家还记得之前我们调试时,尝试点击"同取"复选框改变的值吗? 

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第33张图片

就是上图第3个字节位,而我们再看这4句mov赋值,显然赋值的地址也是按ecx的寄存器累加上去,一位位的设置的,显然mov byte ptr ds:[ecx+0x02], 0x00,这句就是我们需要修改的,把后面的0x00改成0x01即可,如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第34张图片 

 然后右击修改的代码,点击补丁保存修改后的文件,我们可以另起一个名称,如下图:

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第35张图片

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第36张图片

 关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第37张图片

然后我们运行下这个修改后的主程序看看。

关于一个QT程序的简单破解思路(不需要分析信号和槽的方法,通用所有程序的破解思路)_第38张图片

 可以看到,修改成功了,教程到此也写完了,感谢大家的耐心观看,本人不才,但是希望大家有学到新知识,谢谢!

你可能感兴趣的:(开发语言,安全,windows,测试工具,qt)