逆向与IDA调试技巧小结

最近在逆向一个通信协议的实现,具体来说这个协议实现代码在一个windows软件的动态链接库中,该协议的通信过程中加入了随机数以防止重放攻击,我需要逆向找到该协议是如何对随机数进行一系列的计算,从而完成重放攻击。历时四个月终于完成,第一次搞win下逆向就是这么硬核的工作,着实让我费尽心思,更不知道熬了多少个大夜。不过在整个过程中的确学的非常多的知识,但因为并不能公开是何种协议,所以只能抽出整个过程中学到的逆向技巧进行分享。

题目可能会有点大,想正经学习逆向还是要看一些经典的书籍,如:《软件调试》、《加密与解密》、《逆向工程核心原理》等。所以我之后的讨论都局限在我本次工作中学到的东西,主要逆向调试工具为:IDA和dnspy,目标限定为:

  • windows下的一个大型软件
  • 已经确定目标代码位置就在某个动态链接库中
  • 此dll为C#和C++混合编写
  • 无符号表
  • 软件工作是多线程的

而且网上关于如果使用IDA进行动态调试的方法少之又少,《IDA pro权威指南》中也是对动态调试几页带过。一些CTFer也都说调试的话就用GDB了,一般很少用IDA。但是整个工作完成下来,觉得IDA的动态调试非常好用,可以在F5后C代码层次下断点,观察变量,极个别情况才需要对应的汇编。所以本文也会借此机会着重介绍和整理IDA的动态调试方法。闲言少叙,我们打板就唱:

如何找到目标文件

这次任务中,我知道目标方法存在于哪个动态链接库中,但是如果我们不知道呢?

加载动态库的时间

windows下的软件可能会去动态挂载和卸载动态链接库,尤其是大型软件更是如此,所以当我们触发需要逆向的功能时,可以观察这时软件挂载了哪些动态链接库,很有可能就是我们要找的功能实现。利用windows下的调试工具基本都可以看到软件实时加载了哪些动态链接库,如OD

文件名以及符号表

软件也是人写的,如果二进制文件的符号表没有被删除,则可以用IDA这种逆向工具观察符号表中有没有我们要找的功能的名字等。如果去除了符号表,则可以找该软件更老的版本观察,大型软件重构还是很困难的,所以很有可能老版本中存在的符号信息可以给我们一些帮助。最后,还可以根据动态链接库的文件名进行猜测,当然有的软件厂商还可能会将文件名改成无意义的序号,以对抗这种方式的信息泄露。

泄露的内部技术文档

我个人认为,逆向工程,就是人与人的对抗,并不是人和自然的对抗。如果你是这个东西的开发者,或者说你有个这东西的开发文档,那么逆向是没有意义的,也就压根就不需要逆向了。逆向就是你不知道这个玩意是怎么开发的情况下,把这玩意摸清楚,搞明白。所以我觉得,如果你搞到了全部的开发文档,那相当于作弊直接通关。但是如果只搞到部分文档,利用这些去完成后续的逆向工作,还是说的过去的。

如何分析目标文件

拿到了目标动态库之后就是分析了,对于普通的二进制文件,直接丢到IDA里就好了,但是对于一个C#和C++混合的目标,IDA可以给出如下选择:

分析目标的C#代码

利用dnspy

分析目标的C/C++代码

利用IDA

如何调试目标代码

面对一个windows下的动态链接库,调试器肯定是无法直接附加到这个库上,调试器要附加到用到这个库的进程上,也就是我们要搞的那个大型软件。这里我们分别举例IDA和dnspy的动态调试

如何找到关键函数

因为对于随机数的计算最后会通过数据包发送出去,所以这里我们关注的是一个网络发包的功能。

找到前序函数

找到后续函数

如何分析指针以及内存

如何识别常见加密算法

本次工作中遇到了两处常用的加密算法,AES和HMAC_sha256,当然拆开了说是三种。加密算法经常都是好多轮,又各种移位操作等等。不过在各种加密算法中一般都会有一些常量,比如:AES的s盒,sha256的初始iv,HMAC中秘钥异或的opad和ipad。这些东西都是固定的,所以如果在代码中看到了使用这些数据,那么就基本可以确定,这段代码是什么加密算法或者是这个加密算法的一部分。比如:

如何运行IDA F5出的c代码

首先需要defs.h这个头文件

修正栈变量赋值

修正全局变量

IDA使用疑难杂症集锦

函数太长无法F5

函数F5解析失败

发现博客,因为中间出现问题,提示栈帧过长,于是找到博客,解决办法是:在IDA中把函数定义删了,然后重新建一个函数

  • https://introspelliam.github.io/2017/08/02/re/XMAN%E4%B9%8B%E6%97%85-%E9%80%86%E5%90%91%E5%9F%BA%E7%A1%80%EF%BC%88%E4%B8%8A%EF%BC%89/
  • https://introspelliam.github.io/2017/08/03/re/XMAN%E4%B9%8B%E6%97%85-%E9%80%86%E5%90%91%E5%9F%BA%E7%A1%80%EF%BC%88%E4%B8%8B%EF%BC%89/

函数调用栈无法跟踪

  • encrypt3_0的栈帧整个往下偏移,所以到断点时无法看到调用栈,f8出了这个函数即可,父级函数为call_en3_0
  • encrypt3_0虽然栈帧往下偏移,但是栈结构还是老样子