IDA Pro 初步实践

实践1

背景

某软件A,在非全屏显示时带有常规菜单,在全屏下没有常规菜单,但是有顶部工具条,工具条上有菜单和按钮。对于全屏和非全屏的切换可以通过菜单,也可以通过快捷键ctrl + alt + enter进行。


需求

需要将菜单和工具条去除,同时将切换全屏/非全屏快捷键修改为ctrl + alt + shift。


调研

当拿到这个需求的时候,直观上感觉需要逆向,大概也知道一些逆行工具,比如IDA Pro、OllyDbg、x64dbg之类的,经过调研得知:

  • IDA Pro:PE文件静态分析工具,也可以进行调试;
  • OllyDbg:只能调试32位进程;
  • x64dbg:调试64位进程;
  • x32dbg:调试32位进程。

IDA Pro

IDA Pro 是一款功能强大的反汇编和反编译软件,由意大利公司 Hex-Rays 开发。它主要用于分析和理解应用程序的代码和逻辑,支持多种平台和架构,包括 Windows、Linux、macOS、ARM、x86 等。IDA Pro 在反向工程和安全研究领域被广泛应用,常用于恶意软件分析、漏洞分析、软件开发、操作系统内核分析等方面。

主要功能包括:

  1. 反汇编功能:将目标程序的机器代码转换为可读性更高的汇编代码,帮助用户分析程序的代码结构和逻辑。
  2. 反编译功能:通过 Hex-Rays Decompiler 插件,将汇编代码转换为类似 C 语言的高级语言代码,使代码更易于理解和分析。
  3. 调试功能:支持与多种调试器集成,用户可以在程序运行时查看变量、堆栈、寄存器等信息,并设置断点、单步执行等。
  4. 插件支持:用户可以通过插件扩展 IDA Pro 的功能,例如自动化分析、生成脚本等。
  5. 数据库功能:生成和维护程序的数据库,存储函数、变量、注释等信息,便于快速访问和分析。
  6. 跨平台支持:支持多种操作系统和架构,用户可以在不同平台上使用 IDA Pro 进行反汇编和反编译工作。

应用场景:

逆向工程 :分析二进制文件,了解程序的工作原理和逻辑。

恶意软件分析 :通过反汇编和调试,分析可疑文件的行为和潜在威胁。

漏洞挖掘 :发现程序中的安全漏洞和潜在风险。

软件开发:帮助开发者调试和分析编译后的二进制文件。

解决过程

观察这个软件的安装目录,发现有很多QT的Dll,用IDA打开该软件后观察导入表Imports和函数调用,发现确实UI是用QT做的。

  1. 去除常规菜单

QT菜单相关的函数有:

cpp 复制代码
// 创建菜单栏
QMenuBar *menuBar = new QMenuBar(this);
QMenu *fileMenu = menuBar->addMenu("&File");

// 添加菜单项
QAction *newAction = fileMenu->addAction("&New");
QAction *openAction = fileMenu->addAction("&Open");

// 获取主窗口的菜单
QMebuBar* menuBar = QMainWindow::menuBar()

于是在反汇编中查找上述相关函数,找到如下代码:

asm 复制代码
xt:000000014xxxxE89                    call    cs:__imp_?menuBar@QMainWindow@@QEBAPEAVQMenuBar@@XZ ; QMainWindow::menuBar(void) // 返回主窗口的菜单menuBar,返回值放入RAX
.text:000000014xxxxE8F                 mov     rdi, rax   // 再将menuBar放入rdi
.text:000000014xxxxE92                 lea     rbx, [rbp+130h+var_40]
.text:000000014xxxxE99                 nop     dword ptr [rax+00000000h]
.text:000000014xxxxEA0

.text:000000014xxxxEA0 loc_14xxxxEA0:                          ; CODE XREF: sub_14xxxx4B0+A0A↓j
.text:000000014xxxxEA0                 mov     rdx, [rbx]  // rdx存放的是addMenu函数参数
.text:000000014xxxxEA3                 mov     rcx, rdi  // rcx存放的是menuBar,即下面函数调用的this指针(x64应用程序在传参的时候,this指针使用rcx传递)
.text:000000014xxxxEA6                 call    cs:__imp_?addMenu@QMenuBar@@QEAAPEAVQAction@@PEAVQMenu@@@Z ; QMenuBar::addMenu(QMenu *)
.text:000000014xxxxEAC                 add     rbx, 8
.text:000000014xxxxEB0                 lea     rax, [rbp+130h+var_28]
.text:000000014xxxxEB7                 cmp     rbx, rax
.text:000000014xxxxEBA                 jnz     short loc_14xxxxEA0
.text:000000014xxxxEBC                 mov     rdx, rdi
.text:000000014xxxxEBF                 mov     rcx, r14
.text:000000014xxxxEC2                 call    cs:__imp_?setMenuBar@QMainWindow@@QEAAXPEAVQMenuBar@@@Z ; QMainWindow::setMenuBar(QMenuBar *)

基于以上代码,将addMenu函数替换为nop(nop对应的硬编码是0x90),call指令有6个字节:

修改后,通过菜单Edit->Patch Program->Patched bytes 写入原文件,运行后菜单确实去掉了,但副作用是快捷键ctrl + atl + enter 也不生效了。


  1. 去除顶部工具条
    从外观观察来看,工具条应该是一个QWidget,与菜单不同,QWidget可以调用hide、move、resize、setGeometry等函数让UI呈现出消失的效果。
  • 尝试hide

    实际上是去除对show函数的调用,也找到了对这个函数的调用,但是副作用也是全屏状态下快捷键生效,这个情况是无法接收的,因为非全屏下的快捷键虽然也失效了,但是仍然可以用鼠标最大化后变为全屏,而全屏状态下没有快捷键的情况下是无法用鼠标恢复的,所以这一方式不可行。

  • 尝试resize

    找到了对这个函数调用,如下:

如果所示:对这个工具条QWidget先调用了move(QPoint const&)又调用了resize(QSize const&),

于是很容易想到,将resize函数的参数置为0,这样部件就不可见了!

因此两个函数调用是挨着的,两个rcx都是同一个对象的this指针,因为要让部件不可见,所以我们已经不关心move函数了。

将lea rdx, [rsp+38h+arg_8] 通过修改字节,将其修改为lea rdx, [rsp+38h+arg_10],此时rdx存放的就是QSize对象的地址了,即resize函数的参数地址,然后将move替换为nop,再将xxxx2E9E3地址处的字节码改为48 C7 02 00 00 00 00 ,对应的汇编代码为(为什么不直接修改为汇编代码是因为IDA提示不让修改,只能修改bytes):

asm 复制代码
 mov qword  ptr [rdx], 0

这样就将resize函数的QSize对象修改为了一个尺寸为0的对象,然后运行后发现还是不行!

分析后得知QWidget之前调用了setSizePolicy函数来限制最小尺寸,宽度为0显然不合法,所以先去掉了对setSizePolicy的调用。

至此还是不行,工具条仍然有一部分可见,看起来可见的部分应该不是QWidget的一部分。

  • 尝试move
    还是基于上图,move和resize两个函数的调用挨着,这就是一个天然的便利条件!
    于是尝试将resize函数修改为move函数,修改的时候有点巧,按照如下凡是修改后就变成了move函数的地址:

然后按照前面的方法,将QPoint参数的x坐标置为-1000,移动到一个不可见的位置。

地址xxxx2E9E3处的汇编代码通过修改bytes变成:

asm 复制代码
lea  rdx,  [rsp+38h+arg_8]  // 保持不变
mov dword ptr[rdx+4], 0FFFFC18h

这样工具条就隐藏了,快捷键也没有收到影响。


  1. 快捷键

    非全屏下的快捷键的问题,通过spy++找到窗口类名,结合窗口标题起一个后台进程用FindWindow函数找到窗口句柄,向其发送SC_MAXIMIZE消息最大化(实际是全屏),当切换为非全屏时向窗口发送模拟键盘ctrl + alt + enter按下和弹起。

  2. 修改界面字符串

    这个比较简单,只要修改后的字符串长度不超过原字符串,修改的方式就很简单,用shift + F12让IDA列出所有字符串,然后ctrl + F找到修改的字符串,找到其调用的地方,然后修改bytes即可。



2. 实践2

背景

某软件B在某个情况下无法正常使用,会弹出一个提示框,点击确定后退出。

需求

破解这种情况。

思路

  • 弹框可能是MessageBox函数
  • 退出时可能是exit、TerminateProcess、ExitProcess之类的
  • 找到弹框和退出的地方,将其去掉调用

问题

  • IDA 静态分析的时候是不会包含动态加载的DLL
  • 如果进程的检测逻辑(MessageBox和退出)不在exe中怎么办?
  • 启动调试的时候,是否会显示dll的反汇编代码?
  • 隐式加载和显式加载的dll,如果启动exe调试是否会显示其反汇编代码?

最后

该实践没有验证完就不需要验证了,所以没有继续。