在逆向分析某mqtt client软件的时候,由于调用了“Paho Asynchronous MQTT C Client Library”,为了分析获得某硬编码的API参数值,需要在IDA工具里构造struct。下面是分析的过程
1.定位到API结构体的位置
在某次逆向分析某mqtt client软件的时候,我对此处的v17的值很感兴趣,我怀疑是某重要信息的硬编码,我想知道这串字符串到底是用做什么的?
全局搜索v17出现的地方,第一个搜到的是它的定义。
由于结构体地址空间是连续的,我们是可以轻易大胆猜测v16-v22几个命名很可能是结构体的相邻项。
v16很显然是第一个结构体第一个地址,搜索v16出现的地方,试图推测它的结构体名字
从日志中,我们认为推断结构体名字叫MQTTAsync * connect *的可能性比较大。
2.通过查阅资料创建API结构体
前期通过IDA对所有可读字符串的分析知道,本代码调用了“Paho Asynchronous MQTT C Client Library”库,找到库文档,查找名字相似的API介绍。
Paho Asynchronous MQTT C Client Library: MQTTAsync.h File Reference (eclipse.org)
找到了一个很相似的 data struct,“MQTTAsync_connectOptions Struct”。
初步分析了结构体前几项占用空间大小,觉得挺像的。
打开IDA的struct窗口(view-opensubviews-structures),按照库文档里的结构体描述信息,创建struct。
自己构建的struct如下:
最后几项不重要,直接省略了。
这里要注意字节对齐,64位IDA里结构体指针是占用8个字节,因此在will前要自己添加四个字节以对齐。
附:结构体内容解释
typedef struct
{
char struct_id[4]; //MQTC
/** 这个参数必须是 0, 1, 2, 3 4 5 or 6.
* 0 表示不使用SSL,也不使用serverURIs
* 1 表示不使用serverURIs
* 2 表示没有设置版本信息
* 3 表示不使用自动重连
* 4 表示不使用二进制密码,即不设置binarypwd
* 5 表示没有MQTTV5的属性
默认设置这个值为6
*/
int struct_version;
//保活时间间隔
int keepAliveInterval;
//1 丢失连接时清楚所有这个连接的信息,包括订阅等,0与之相反,使用非5.0的时候设置这个参数
int cleansession;
int maxInflight; //这个值设置最大的在飞行中的消息数量
MQTTAsync_willOptions* will; //这个参数设置断开连接时的临终遗嘱消息,断开连接时发送消息到LWP topic
const char* username; //用户名
const char* password; //密码
int connectTimeout; //连接超时时间
int retryInterval; //重发未发送的消息的时间间隔,以秒为单位的内部时钟间隔
MQTTAsync_SSLOptions* ssl; //指向ssl设置参数的结构体指针,为NULL时表示不使用SSL
MQTTAsync_onSuccess* onSuccess; //连接成功时调用
MQTTAsync_onFailure* onFailure; //连接失败时调用
void* context; //赋值为client
int serverURIcount; //serverURIs数组大小
char* const* serverURIs; //服务器url数组,null结尾的字符串数组,地址格式为protocol://host:port。protocol可以是tcp或者ssl,host可以是ip地址或者域名
int MQTTVersion; //MQTT版本信息
int automaticReconnect; //自动重连
int minRetryInterval; //最小的重试间隔,以秒为单位,没失败一次,值增加一倍
int maxRetryInterval; //最大的重连时间间隔,最小重连时间间隔超过这个值时停止
struct {
int len; /**< binary password length */
const void* data; /**< binary password data */
} binarypwd; //二进制密码设置,目前没去了解
int cleanstart; //使用5.0的时候设置这个参数
//MQTTV5的一些属性设置
MQTTProperties *connectProperties;
MQTTProperties *willProperties;
MQTTAsync_onSuccess5* onSuccess5;
MQTTAsync_onFailure5* onFailure5;
} MQTTAsync_connectOptions;
3.关联自己定义的结构体并成功得到API的关键项的值
通过前面分析已经知道v16是结构体的第一项,现在修改v16定义为刚创建的结构体
按F5刷新页面,可以看到我们感兴趣的字符串已经显示出它本来的含义了。
这是一个硬编码的username,这个发现对我们接下来继续逆向分析很有意义。