zoukankan      html  css  js  c++  java
  • 连连看辅助

    转载自大神CSDN博主「九阳道人」

    版权声明:本文为CSDN博主「九阳道人」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_31507523/article/details/88309060

    QQ连连看单机版辅助制作全流程

    最近在15PB学习逆向,分析了个小游戏并写出了辅助工具,在这里总结下全流程。

    游戏:QQ连连看(单机版1.2)

    完成目标:

    1.去除广告

    2.完成指南针、炸弹消除的功能

    3.编写注入程序和游戏辅助的DLL

    使用工具:VS2017、OD、CE、PEID、Spy++

    分析环境:Win7虚拟机

    首先在百度上搜索“QQ连连看单机版”随便下了个V1.2版的,把它解压到桌面然后进入文件夹。

    仔细观察下都有些什么文件,这很重要,有可能得到一些有利于破解的信息。

    1.在这里观察到有两个可执行的EXE文件、两个音乐文件夹,这都是比较重要的信息。

    1565407736276

    2.用PEID擦看下这两个EXE程序基本信息,得以得出"kyodai.exe"程序是VC6.0的编译器编写的,游戏一般都是C++语言编写,而"QQ连连看单机版V1.2.exe"是一个加壳的程序,常见压缩壳ASpack这就忽略它,也能分析。

    1565407757730

    1565407773830

    3.双击"kyodai.exe"程序后回崩溃如图。

    1565407789849

    4.那么就确定了开始游戏的程序"QQ连连看单机版V1.2.exe",打开之后会弹出广告窗口,再之后游戏就运行了。

    1565416222982

    5.退出游戏,打开”任务管理器”,在运行游戏观察进程的变化,发现在弹出广告窗口时只有一个与游戏相关的进程”QQ连连看单机版V1.2.exe”

    1565416238046

    6.点击继续进入游戏之后发现此进程消失,而多了另一个很熟悉进程"kyodai.exe"

    1565416250432

    7.由于最终运行的还是"kyodai.exe"程序,但是点击它又运行不了。而通过其他程序却可运行它, 由此猜想"kyodai.exe"在运行时,”QQ连连看单机版V1.2.exe”打开它并对它的内存进行了修改,使他能正常运行。在这说明下这是老编译器写的游戏不存在随机基址,很多地址都是固定的。

    猜想它可能使用了CreateProcessAW函数打开进程,那么开始调试,使用OD开打”QQ连连看单机版V1.2.exe”,Ctrl+G搜索CreateProcessAW在2个函数上都按F2设置断点,运行之后发现在CreateProcessA函数上断下,观察堆栈数据,发现"kyodai.exe"程序被它以挂起的方式打开了。

    1565416281645

    8.很多恶意程序都是这样的套路,那么就容易猜想了,挂起了程序之后就可能修改内存数据,之后恢复程序,那再就来搜索一下修改内存数据函数WriteProcessMemory下断点,运行,在次停下了再观察堆栈数据,发现3个重要的数据,意为在"kyodai.exe"程序中的0043817A 地址处修改1个字节的数据。

    1565416294454

    9.选中该Buffer右键查看其内容是”0”。

    1565416304273

    10.然后在搜索一个唤醒线程的函数ResumeThread下断点,运行之后果然走到了这里。

    1565416315593

    11.那么我们现在就已经分析出了”QQ连连看单机版V1.2.exe”的运行机制,它首先弹出广告,然后再执行CreateProcessA -> WriteProcessMemory -> ResumeThread操作打开"kyodai.exe"程序,那么我们现在就可以模拟它的执行流程编写程序打开它

    打开VS2017编写程序如下

    #include "pch.h"
    #include <iostream>
    #include <windows.h>

    //kyodai.exe程序的路径
    #define CATALOG L"C:\Users\15pb-win7\Desktop\连连看\kyodai.exe"
    int main()
    {
    STARTUPINFO si = {};
    //操作进程的信息结构体
    PROCESS_INFORMATION pi = {};
    //打开进程
    CreateProcess((LPWSTR)CATALOG, 0, 0, 0, FALSE,
    CREATE_SUSPENDED,//此参数为挂起主线程
    0, 0, &si, &pi);
    DWORD dwWrite = 0;
    //修改1字节的数据
    WriteProcessMemory(pi.hProcess, (LPVOID)0x43817A, "x0", 1, &dwWrite);
    //恢复线程
    ResumeThread(pi.hThread);
    return 0;
    }

    12.本人认为这段程序对咱们新手来说比较有用。在这里要注意的是”kyodai.exe”所在路径每个人的可能都不一样,编译程序后运行,打开游戏成功!

    1565416380190

    13.当然也可以直接使用16进制文件编辑器直接找到3817A(因为默认加载基址0x400000需要减去)地址把里面的数据改为“00”,我使用的工具是010Editor。

    1565416391301

    14.接下来正式分析游戏关键功能了,正常的程序汇编中CALL xxx基本都是函数,运行游戏之后多玩几把测试一下游戏玩法,游戏右上角道具栏有指南针,多刷新几次地图可能会出现炸弹,把炸弹消除掉道具栏里会出现炸弹,而且道具有数量限制。

    1565416403574

    15.我用了CE工具查找道具基址,很遗憾找了10多分钟没找到,只有换一种方法了,点击道具的时候会发出声音,那么音乐文件的名字是字符串,搜索字符串可以是个切入点。

    1565416412285

    16.还有点击练习的时候地图会随机刷新,那么肯定会用到rand这个随机函数,亲测两种方法都能达到一样的效果找到关键点,在这里总结下查找API的方法,使用OD打开或者附加”kyodai.exe”程序先运行起来,再在OD中Ctrl+G搜索rand函数下断点,点击游戏中的练习后断了下来。

    1565416426499

    17.然后点击OD菜单栏上的”K”进行栈回溯分析。

    1565416434699

    观察发现有2个上层调用(有程序的名字的2个),他们的关系是0x41A080处的函数调用0x41CAF2的函数,0x41CAF2的函数调用rand。这里说的比较啰嗦,反正在”K”中越靠下就越是外层的函数。

    1565416445825

    18.双击进入第一个地址并在上面设置断点,然后再点K双击进入第2个地址设置断点然后把之前rand的断点删除

    1565416463248

    1565416472669

    19.F9运行后再次在游戏中点击练习会在第一个断点0x41A080地址上停下这里是一个函数(CALL),要重点关注一下它上面行代码MOV ECX, EDI 由于这是C++所写的程序,它都会使用ECX这个寄存器传递this指针也就是传递一个对象,之后看见只要对ECX寄存器操作的代码都要留意一下。按F7进入这个函数,先Ctrl+A让OD帮我们分析一下该模块,发现一开始把ECX的值给了ESI,先不管这继续单步然后没几步就看见了熟悉的字符串”strat.wav”(字符串搜索也能定位到这个函数里来),这已经证明这个函数就是初始化游戏的函数。

    1565416483525

    20.然后再快速单步很快就能发现刚才所下的第二个断点,发现rand下面有一个 memcpy复制内存的函数,先大致走一遍注意观察OD中右上角寄存器的变化,观察寄存器上的值的内存的变化,走完了好像什么也没发现…然后再重新运行再多单步跟踪几次这个函数总会发现一些什么的。

    1565416494177

    21.再次跟踪这个函数,实际上刚才说过要注意ECX的值的传递,在这个函数里ECX传递给了ESI,那么我们要对所有操作ESI的代码留意一下,在加上我们下的第二个断点0x41CAF2所在附近,我们就要对它附近ESI重点关注了,在0041CAFC 地址处的这一行代码对ESI进行了访问,走一步之后我们发现EAX的是0012BB50。

    1565416504710

    22.那么再选中它右键->数据窗口跟随 查看0012BB50地址里面有些什么数据。

    1565416514028

    23.然后走到0041CB10 地址处,这里会调用memcpy复制内存,走一步观察0012BB50地址里的数据除了第一个DC 00…没变,后面都被填充成某种规律的010101…

    1565416524666

    24.在继续走两步经过0041CB20 地址出的函数后惊讶的发现0012BB50地址里那一大串数据又被刷新,到此我们大胆猜测0012BB50这就是游戏的地图数组基地址。

    1565416536898

    25.为了验证是否正确按F9把游戏运行起来观察地图与该内存数据的是否有联系(观察地图和该内存时刷新出一个特点鲜明的地图较好,比如地图右上角有两个靠的近相同的东西等)我多刷新了几次得到一个左上角和右下角都被填满了的地图,观察OD中的内存(记得点击下OD数据才会刷新)也跟着改变了,貌似还找到了数组的边界!0012BB58(第一个点)

    1565416544743

    26.再刷新几次地图找到第一行有两个连着的相同的东西,发现地图和OD中的内存非常相似。

    1565416553410

    27.为了确认 我们把游戏中的物品点击消除掉在观察内存发现果然被清0了那么可以确定0012BB50这就是地图数组基地址了,并且我们得到了0012BB58就是地图数据的起始位置。

    1565416563802

    28.接下来分析指南针和炸弹,指南针功能是可以帮助玩家找到2个相同物品,炸弹是找到2个相同的物品并消除,这2个道具不管是谁都会遍历地图数组,才能实现他们的功能,所以在0012BB58地址处右键下一个内存访问断点。

    1565416574502

    29.先在OD中按ALT+B把其他断点禁止或者删掉,然后运行游戏点击指南针,程序会停下来。点击”K”进行栈回溯分析观察他的上几层调用函数发现有5个那么每个都点进去设置上断点,然后右键选中先把0012BB58的内存断点给删除掉。

    1565416584279

    30.再F9运行OD再运行几次发现都停在004292A5 地址上那么这肯定是不需要的函数把它断点去掉,再运行2次,点不动了需要点击游戏界面,一点就在OD中断下来了 因此判断0040CACA断点也不是我们想要的,同样的继续运行几次还是都停在了0041AF11 处,这也是无用地址去掉断点,再F9游戏就运行起来了,这个过程多调试几次就会明白的。

    因此0041DE5C和0041E76C 处的函数就可能是我们能利用关键函数,现在想要写代码完成指南针的功能,只要找到这两处地址的函数调用时所需要的参数,那就可以模拟出指南针的功能。

    重新运行起来,点击游戏中的指南针,会在0041DE5C 处的代码断下,选中这一行按Enter建进入这个函数一直往下拉找到末尾RETN观察返回值发现RETN 0xC 也就是说0041DE5C 地址处的函数调用需要3个参数,在观察堆栈得出参数是 0,0,F0。

    1565416599545

    31.为了确认这3个参数是不是可变的,再次运行游戏,再点击指南针,再次在0041DE5C 停下发现堆栈里的参数还是这么多没有改变,那我就决定调用了这个函数模拟指南针功能了,写好注释。 其实在第2个断点0041E76C 处的参数更少,只有2个,但是在那里断下来之后查看堆栈里的参数也不知道是些什么,所以放弃它把它的断点去掉。

    相同的原理我再找到炸弹的调用地址,多刷新几次游戏,找到有可以消除炸弹地图,把炸弹消除了道具栏中就出现了炸弹。

    1565416611362

    32.同样运行游戏之后在0012BB58下内存访问断点,再点击游戏道具栏里的炸弹,断下之后再次按”K”进行栈回溯分析,发现这几个地址很眼熟,和之前找指南针的栈回溯基本一致 只有一个不一样,那么无用几个地址不用管他,我们在0041DE5C 处下断点,刷新游戏地图找到一个有炸弹的地图,再使用炸弹之后停下观察堆栈参数的变化,发现参数是0,0,F4。

    1565416620756

    由此我们可以对比下两次调用这个函数的参数

    调用指南针是0,0,F0

    调用炸弹是0,0,F4

    得出结论第3个参数就是游戏的道具类型,0041DE5C 地址处的函数功能应该就是使用道具。

    33.谈一下我对辅助中的的DLL,注入程序,原程序(这里就是游戏程序)他们关系的理解:我们想要DLL里面的功能在原程序种实现,直接来是不行的,需要通过注入程序把DLL注入到原程序中,这时DLL功能才会对原程序产生影响。 这就像热发烧了打针一样,针筒里的药水就是DLL,针筒就是注入程序,人体就是原程序。

    34.接下里就是编写辅助了,在这需要实现指南针和炸弹的功能,之需要模拟调用0041DE5C 地址处的函数就OK了。

    我用的是VS2017首先创建一个MFC的DLL程序,选择在静态库中使用MFC,这个选项也可以在属性->常规里面更改。

    1565416634318

    35.使用到的关键API如下

    FindWindow获取窗口句柄

    SetWindowLong设置窗口回调

    _beginthreadex创建线程便于弹窗

    因为要在游戏中弹出一个窗口,所以要添加一个资源Dialog

    窗口回掉原型为

    LRESULT

    CALLBACK

    DefWindowProc(

    In HWND hWnd,

    In UINT Msg,

    In WPARAM wParam,

    In LPARAM lParam);

    要实现的辅助功能主要就是在这个窗口回掉函数里面实现。

    36.现在先使用VS中的自带工具Spy++先查找游戏的,窗口名,和类名,用于的FindWindow函数获取窗口句柄。

    把Spy++中的望远镜拖到游戏窗口上可以可到,窗口名。

    1565416650983

    1565416665152

    37.再次分析下0041DE5C 地址的函数这里的参数是3个但之前也说过C++使用ECX传递对象,所以我们必须得到ECX的值,经过多次运行发现右上角的ECX值一直是0012A688所以我们直接使用这个固定值。

    1565416676992

    38.使用_asm内嵌汇编调用那个关键函数,0041DE5C 地址处汇编是CALL [EAX+0x28] ,在内存窗口中Ctrl+G搜索EAX+0x28 里面的值为0041E691(以小端方式读) 这就是需要调用函数的地址。

    LRESULT
    CALLBACK
    MyDefWindowProc(
    _In_ HWND hWnd,
    _In_ UINT Msg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam)
    {
    //指南针消息
    if (Msg == WM_SIGN1)
    {
    _asm
    {
    MOV ECX, 0X12A688;    //this指针
    PUSH 0XF0;            //参数3是道具类型
    PUSH 0;               //参数2
    PUSH 0;               //参数1
    MOV EAX, 0X0041E691;
    CALL EAX;             //指南针函数
    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    if (Msg == WM_SIGN2)
    {
    _asm
    {
    MOV ECX, 0X12A688;
    PUSH 0XF4;
    PUSH 0;
    PUSH 0;
    MOV EAX, 0X0041E691;
    CALL EAX;//炸弹函数
    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam);
    }

    39.说明一下,为了在辅助程序的窗口发送消息,我在头文件stdafx.h中自定义了两个消息,然后编译生成DLL就OK。

    1565416728499

    1565416737795

    1565416746284

    40.最后编写远程注入程序,就创建一个控制台程序就行了,要注意的是在这里要在属性里选择C+±>代码生成把运行库改为多线程调试(MTd) 就跟上面的DLL静态编译一样,如果不这样,我们写的程序在别人的电脑上可能就运行不了。

    1565416757512

    41.远程线程注入的代码基本都固定,死记硬背就行了,在此贴上。

    #include "pch.h"
    #include <windows.h>
    #include <string.h>

    //注入DLL函数
    void InjectDll(HWND hWnd, const char* DllPath)
    {
    //获取要注入的进程的PID并打开它
    DWORD dwPid = 0;
    GetWindowThreadProcessId(hWnd, &dwPid);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    if (!hProcess)
    {
    printf("打开进程失败 ");
    getchar();
    return;
    }
    //在注入的进程中分配一块虚拟内存
    LPVOID lpAddr = VirtualAllocEx(hProcess,
    NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
    if (!lpAddr)
    {
    printf("分配内存失败 ");
    CloseHandle(hProcess);
    getchar();
    return;
    }

    //把dll路径写入到目标进程空间中
    DWORD dwWrite = 0;
    WriteProcessMemory(hProcess, lpAddr, DllPath,
    strlen(DllPath) + 1, &dwWrite);
    if (strlen(DllPath) + 1 != dwWrite)
    {
    printf("dll路径写入失败 ");
    VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    getchar();
    return;
    }

    //创建远程线程
    HANDLE hThread = CreateRemoteThread(hProcess, 0, 0,
    (LPTHREAD_START_ROUTINE)LoadLibraryA,
    lpAddr, 0, 0);
    if (!hThread)
    {
    printf("远程线程创建失败 ");
    VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    getchar();
    return;
    }

    //等待线程结束(无穷大毫秒)
    WaitForSingleObject(hThread, INFINITE);

    //释放
    VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    }

    //动态库DLL的名字
    #define DLLNAME "\01连连看辅助.dll"

    int main()
    {
    //dll的全路径名
    char dllNamePath[256] = {};
    //获取DLL的全路径
    GetCurrentDirectoryA(sizeof(dllNamePath), dllNamePath);
    strcat_s(dllNamePath, DLLNAME);
    //获取游戏窗口句柄
    HWND Handle = FindWindow(NULL, L"QQ连连看");
    //调用远程注入函数
    InjectDll(Handle, dllNamePath);
    return 0;
    }

    42.这里说下游戏辅助常用的API函数GetCurrentDirectoryA这个API可以获取到本程序的当前目录的字符串,只要把要DLL放在与注入程序同一个目录了,直接运行注入程序就注入DLL了,这个非常方便。

    1565416812292

    43.把DLL文件和注入程序生成放在同一个目录下,先打开游戏,然后点击注入程序,单机版连连看的辅助就顺利完成了,运行秒杀效果如图。

    1565416820759

    最后感谢15PB的栽培

    游戏和源码网盘链接: 链接:https://pan.baidu.com/s/1LyAn27Y__beBpCewMIr-WA 提取码:oq08

  • 相关阅读:
    rstudio执行代码&注释
    Python,mac下如何安装pip、ipython
    利用MATLAB批量完成科研数据处理
    稳定性与鲁棒性区别(转)
    Matlab subs函数的用法
    matlab替换
    opencv-python(cv2)
    ConvolutionalLSTMNetwork: AMachineLearning ApproachforPrecipitationNowcasting论文笔记
    ROLO:Spatially Supervised Recurrent Convolutional Neural Networks for Visual Object Tracking(二)
    ROLO:Spatially Supervised Recurrent Convolutional Neural Networks for Visual Object Tracking(一)
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11331497.html
Copyright © 2011-2022 走看看