Windows逆向分析实战:使用CE+OD获取用户信息

序:

很多想学软件逆向分析的朋友们,初学者往往看到一大堆的技术资料,直接就懵了。本文以一个简单的例子,演示一下使用CE+OD进行内存的获取,然后使用Qt进行界面显示,让初学者简单了解逆向分析的流程,并且一步步自己进行手动实现,让初学者有一些成就感,避免直接上来就是技术文档打击到学习的热情。

一、准备工作:

CheatEngine 简称CE 用来定位数据;
Ollydbg 简称OD 用来动态调试;
微信版本:3.1.0.67
Qt:5.13.0用来开发界面(我喜欢用Qt做界面,习惯使用MFC的朋友们也可以使用MFC,核心部分不影响)
资源可能不太好找,这里放上我使用的逆向分析三件套:CE+OD+IDA

二、使用CE获取找偏移地址

1、登陆电脑微信,打开CE软件,点击电脑图标,选择进程,选中WeChat.exe,点Open按钮;
Windows逆向分析实战:使用CE+OD获取用户信息
2、在微信找到自己的登陆信息:LaoWang
Windows逆向分析实战:使用CE+OD获取用户信息

3、选择Value Type为String,将上一步中的用户名输入进去,然后点击First Scan按钮进行查找;
Windows逆向分析实战:使用CE+OD获取用户信息

4、查看搜索结果有绿色的,说明用户名偏移是固定的,此处为10F235FC
Windows逆向分析实战:使用CE+OD获取用户信息

5、查看该偏移地址附近的信息
Windows逆向分析实战:使用CE+OD获取用户信息

6、该偏移地址附近的信息,有电话、地址信息、微信号等,说明个人信息是放在一起的;
Windows逆向分析实战:使用CE+OD获取用户信息

二、使用OD查看更多内存信息

1、打开OD软件,File—>Attach,在弹出界面选择微信进程,然后点击Attach按钮;
Windows逆向分析实战:使用CE+OD获取用户信息
2、我们在CE中搜索到的内存地址是:10F235FC,
(1)在OD界面我们使用dc 10F235FC命令,查找该内存地址附加的文本
Windows逆向分析实战:使用CE+OD获取用户信息
(2)在OD界面我们使用dd 10F235FC命令,查找地址附加的指针所指向的文本(在文本较长的情况下,可能会使用指针)
Windows逆向分析实战:使用CE+OD获取用户信息
Windows逆向分析实战:使用CE+OD获取用户信息
可以看到wxid指针偏移地址是:10F23A18

四、计算偏移地址

1、计算偏移地址的方法:
偏移=内存地址-模块基址

2、在CE中查找模块:按下图步骤,可以看出内存地址所对应的模块为WeChatWin.dll;
Windows逆向分析实战:使用CE+OD获取用户信息
3、在OD中查找模块基址:View—>Executable modules,找到WeChatWin.dll对应的基址为:0F680000

 Base=0F680000
 Size=01B40000 (28573696.)
 Entry=10529FDC WeChatWi.<ModuleEntryPoint>
 Name=WeChatWi
 File version=3.1.0.67
 Path=D:\Program Files (x86)\WeChat\WeChatWin.dll

Windows逆向分析实战:使用CE+OD获取用户信息
4、计算偏移
通过上面我们已经知道:
模块基址:0F680000

那么偏移地址
用户名:10F235FC - 0F680000 = 18A35FC
wxid指针:10F23A18 - 0F680000 = 18A3A18

其他信息可以参考此方法,根据需要进行计算

五、代码实现

1、新建Qt工程
Windows逆向分析实战:使用CE+OD获取用户信息
2、用Qt Designer打开MainWindow.ui,拖入一个QTextBrowser和一个QPushButton,并进行布局;
Windows逆向分析实战:使用CE+OD获取用户信息

3、在mainwindow.h类中添加一个槽和四个方法

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <Windows.h>


namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    //按钮点击槽
    void onPushButtonClicked(bool);

private:
    //根据进程名字获取进程id
    DWORD FindProgressPidByName(const char* progressName);
    //获取dll基地址
    DWORD GetDLLBaseAddress(DWORD processID, const wchar_t* moduleName);
    //获取指针指向的内存
    DWORD getIntByAddress(HANDLE hProcess, DWORD address);
    //获取内存地址对应的文本
    QString getStrByAddress(HANDLE hProcess, DWORD address);

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

4、方法的实现
可以使用函数ReadProcessMemory在外部进程内存的读取
(1)根据进程名字获取进程id

/**
 * @brief 根据进程名字获取进程id
 * @param progressName
 * @return
 */
DWORD MainWindow::FindProgressPidByName(const char* progressName)
{
    DWORD processID = 0;

    PROCESSENTRY32 pe32 = { 0 };
    pe32.dwSize = sizeof(PROCESSENTRY32);

    // 获取所有进程的信息
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

    // 拿到第一个进程的信息
    if (Process32First(hSnapshot, &pe32) == TRUE)
    {
        do
        {
            USES_CONVERSION;
            // 进程名字是progressName就返回
            if (strcmp(progressName, W2A(pe32.szExeFile)) == 0)
            {
                processID = pe32.th32ProcessID;
                break;
            }
            // 进程名字不是progressName,获取下一个进程信息
        } while (Process32Next(hSnapshot, &pe32));
    }

    CloseHandle(hSnapshot);

    return processID;
}

(2)根据进程ID和模块名获取dll基地址

/**
 * @brief 根据进程ID和模块名获取dll基地址
 * @param processID
 * @param moduleName
 * @return
 */
DWORD MainWindow::GetDLLBaseAddress(DWORD processID, const wchar_t *moduleName)
{
    DWORD moduleBaseAddress = 0;

    // 获取进程ID processID 对应的进程信息
    HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processID);
    if (hProcessSnapshot == INVALID_HANDLE_VALUE) return moduleBaseAddress;

    MODULEENTRY32 me32;
    SecureZeroMemory(&me32, sizeof(MODULEENTRY32));
    me32.dwSize = sizeof(MODULEENTRY32);

    // 遍历进程的模块信息
    while (Module32Next(hProcessSnapshot, &me32))
    {
        me32.dwSize = sizeof(MODULEENTRY32);

        // 判断是不是目标模块moduleName
        if (!_tcscmp(me32.szModule, moduleName))
        {
            moduleBaseAddress = (DWORD)me32.modBaseAddr;
            break;
        }
    }

    CloseHandle(hProcessSnapshot);

    return moduleBaseAddress;
}

(3)根据进程和指针地址获取内存地址

/**
 * @brief 根据进程和指针地址获取内存地址
 * @param hProcess
 * @param address
 * @return
 */
DWORD MainWindow::getIntByAddress(HANDLE hProcess, DWORD address)
{
    DWORD intValue = 0;

    ReadProcessMemory(hProcess, (LPVOID)address, &intValue, 4, 0);

    return intValue;
}

(4)获取内存地址对应的文本

/**
 * @brief 获取内存地址对应的文本
 * @param hProcess
 * @param address
 * @return
 */
QString MainWindow::getStrByAddress(HANDLE hProcess, DWORD address)
{
    QString csValue = "";

    char cValue[500] = { 0 };
    if (ReadProcessMemory(hProcess, (LPVOID)address, cValue, 500, 0))
    {
        csValue = QString(cValue);
    }

    return csValue;
}

小结

流程是
1、通过CE+OD获取偏移地址
2、通过ReadProcessMemory接口读取内存数据
3、使用Qt开发界面,显示读取的信息

上边写的很详细,源码注释也很详细,相信读者读懂难度不大,这里放上源码

上一篇:python中partial用法


下一篇:嵌入式Qt中实现串口读取的事件通知方法