游戏安全入门-扫雷分析远程线程注入

news2024/11/15 7:21:41

前言

无论学习什么,首先,我们应该有个目标,那么入门windows游戏安全,脑海中浮现出来的一个游戏 – 扫雷,一款家喻户晓的游戏,虽然已经被大家分析的不能再透了,但是我觉得自己去分析一下还是极好的,把它作为一个小目标再好不过了。

我们编写一个妙妙小工具,工具要求实现以下功能:时间暂停、修改表情、透视、一键扫雷等等。

本文所用工具:

Cheat Engine、x32dbg(ollydbg)、Visual Studio 2019

扫雷游戏分析

游戏数据在内存中是地址,那么第一个任务,找内存地址

打开CE修改器

修改时间->时间暂停

计数器的时间是一个精确的值,所以我们通过精确数值扫描出来,游戏开始之前计数器上的数是0,所以我们扫描0。

image.png

时间在变化,选择介于什么数值之间再次扫描

image.png

可得 0x100579c — winmine.exe+579C

image.png

我们发现这个数据都是直接通过基址 + 固定偏移能直接得到的。

然后我们对数据去找 是什么改写了这个地址,得到一个指令和指针:

image.png

时间:0x100579c

修改表情 - 没啥用

修改表情这个功能怎么搞我觉得还是很容易想到的,这个按钮的作用是重新开始游戏,开始游戏,游戏胜利,游戏失败。

(表情的状态被分成了两个变量(4byte)来控制)

所以它是一种状态,所以我们通过0和1进行扫描,游戏进行状态输入1进行扫描,还原游戏之后输入0进行扫描。

首先是游戏进行状态,输入1进行扫描

image.png

再点击表情,将游戏还原,输入0开始扫描

image.png

如此反复进行扫描,得到表情的内存地址

0x1005164 – winmine.exe+5164

image.png

但是嘞,修改成2或者3,表情没有心得反应,所以控制游戏胜利和游戏失败的是其他的地址,我们知道,一般来说,一个功能的代码在内存中基本上都是连续的,(就像你修改一个游戏的血量,浏览血量内存块,你可以发现怒气,蓝量等内存地址)

所以,我们浏览内存

image.png

image.png

0x1005164-4 = 0x1005160

修改为3,发现出现了戴墨镜的表情(游戏胜利)

但是这个胜利知识一个状态,并不能说明扫雷完成.

image.png

表情:0x1005160与0x1005164

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

透视 - 显示雷区

思考游戏结束的时候会自动显示所有的雷,因此我们动态调试,看看在哪个函数调用之后会显示所有的雷

image.png

image.png

经过几次的动态调试之后发现:0x2F80函数是我们要找的结果。

image.png

一键扫雷

通过透视,我们玩一把游戏,使得游戏胜利(点完最后一个)

image.png

image.png

然后后两个函数,是破纪录跟英雄榜的函数

image.png

image.png

ret来到了这儿,游戏通关了,来到了这儿,可以知道,这个0x347c就是判断输赢的函数

并且通过调试发现由一个参数 0 1 来控制,所以跟透视差不多,带个参数线程回调就完了

image.png

编写妙妙小工具

怎么实现这个工具呢,当然是选择DLL注入

那么dll 怎么注入进去呢,这里选择远程线程注入

这里先简单介绍下什么是远程线程注入

前置知识-动态调用dll

主要就是这几个个 API:

LoadLibraryA

加载指定 DLL 并返回模块句柄,参数为字符串,就是 dll 的路径。

GetProcAddress

获取指定 dll 的导出函数的地址。

第一个参数是模块句柄,第二个参数是模块函数,返回值为函数的地址。

通过这两个函数,我们可以拿到所有函数的地址,然后就能进行调用。

CreateThread - 远程线程注入

里面几乎只有一个参数,那就是线程回调函数,然后当然还有返回地址,返回线程 id 啥的,这里我们都可以不用管,几乎是与 Linux 的创建线程函数一致。

还有一个远程版本的叫 CreateRemoteThread,它可以给别的进程创建一个线程并可以在本进程创建那个进程调用的回调函数。我们可以在回调函数中加载指定的 dll,在 dllmain 的入口当中,有一个 switch 的四个选项。

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,//指向自身的句柄
                       DWORD  ul_reason_for_call,//调用原因
                       LPVOID lpReserved//隐式加载or显式加载
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH://附加到进程上时执行
    case DLL_THREAD_ATTACH://附加到线程上时执行
    case DLL_THREAD_DETACH://从线程上剥离时执行
    case DLL_PROCESS_DETACH://从进程上剥离时执行
        break;
    }
    return TRUE;
}


我们可以在 DLL_PROCESS_ATTACH 的选项中加入代码,让它在加载的时候调用执行。

那么我们的步骤是:

  1. 打开指定进程获得句柄
  2. 开辟远程进程的空间,分配可读可写段。
  3. 调用 WriteProcessMemory 将 dll 路径写入该内存区域。
  4. 创建远程线程,回调函数使用 LoadLibrary 加载指定 dll。
  5. 等待返回(loadLibrary返回)
  6. 释放空间
  7. 释放句柄
  8. 返回结果
demo:
void Inject(DWORD ProcessId, const char* szPath)
{
    //1.打开目标进程获取句柄
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    printf("进程句柄:%p\n", hProcess);
    //2.在目标进程体内申请空间
    LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    //3.写入DLL路径
    SIZE_T dwWriteLength = 0;
    WriteProcessMemory(hProcess, lpAddress, szPath, strlen(szPath), &dwWriteLength);
    //4.创建远程线程,回调函数使用 LoadLibrary 加载指定 dll
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpAddress, NULL, NULL);
    //5.等待返回(loadLibrary返回)
    WaitForSingleObject(hThread, -1);
    //6.释放空间
    VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
    //7.释放句柄
    CloseHandle(hProcess);
    CloseHandle(hThread);
    //返回结果
    AfxMessageBox(L"完成");
}

编写DLL注入器

#include<windows.h>
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<TlHelp32.h>
DWORD FindProcess() {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32 = { sizeof(pe32) };
    BOOL ret = Process32First(hSnap, &pe32);
    while (ret)
    {
        if (!wcsncmp(pe32.szExeFile, L"mine.exe", 11)) {
            printf("Find winmine.exe Process %d\n", pe32.th32ProcessID);
            return pe32.th32ProcessID;
        }
        ret = Process32Next(hSnap, &pe32);
    }
    return 0;
}
void Inject(DWORD ProcessId, const char* szPath)
{
    //1.打开目标进程获取句柄
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    printf("进程句柄:%p\n", hProcess);
    //2.在目标进程体内申请空间
    LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    //3.写入DLL路径
    SIZE_T dwWriteLength = 0;
    WriteProcessMemory(hProcess, lpAddress, szPath, strlen(szPath), &dwWriteLength);
    //4.创建远程线程,回调函数使用 LoadLibrary 加载指定 dll
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpAddress, NULL, NULL);
    //5.等待返回(loadLibrary返回)
    WaitForSingleObject(hThread, -1);
    //6.释放空间
    VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
    //7.释放句柄
    CloseHandle(hProcess);
    CloseHandle(hThread);
}

int main() {
    DWORD ProcessId = FindProcess();
    while (!ProcessId) {
        printf("未找到扫雷程序,等待两秒中再试\n");
        Sleep(2000);
        ProcessId = FindProcess();
    }
    printf("开始注入进程...\n");
    Inject(ProcessId, "E:\\CODE\\wimine\\Mine\\release\\Mine.dll");
    printf("注入完毕\n");

}

编写DLL

这里我们采用MFC DLL 基于对话框 (dialog)的方式编写(简单),使用静态编译的方式

image.png

image.png

然后我们需要在资源窗体,新建一个 Dialog ,简单包装一个界面

image.png

这样我们在加载窗体的时候需要创建一个窗体类对象用它的 DoModal 方法去显示,用线程回调的方式加载并且初始化InitInstance

DWORD WINAPI DlgThreadCallBack(LPVOID lp) {
    MineDlg* Dlg;
    Dlg = new MineDlg();
    Dlg->DoModal();
    delete Dlg;
    FreeLibraryAndExitThread(theApp.m_hInstance, 1);
    return 0;
}
// CMineApp 初始化
BOOL CMineApp::InitInstance()
{
    CWinApp::InitInstance();
    ::CreateThread(NULL, NULL, DlgThreadCallBack, NULL, NULL, NULL);
    return TRUE;
}

时间暂停

上面我们找到了它控制时间增加的指令,我们把它们全部 NOP 掉,就可以实现时间暂停

写两个按钮,创建下面的事件实现时间暂停开关。

image.png

DWORD GetBaseAddr() {
    HMODULE hMode = GetModuleHandle(nullptr);
    //LPWSTR s = (LPWSTR)malloc(0x100);
    //wsprintf(s, L"基址:%p", hMode);
    //AfxMessageBox(s);
    return (DWORD)hMode;
}

void MineDlg::OnBnClickedButton1() // 时间暂停
{
    // TODO: 在此添加控件通知处理程序代码
    auto BaseAddr=GetBaseAddr();
    DWORD TimeOffset = 0x579C;
    DWORD TimeInsOffset = 0x2FF5;
    DWORD InsLen = 6;
    DWORD old;
    VirtualProtect((void*)(BaseAddr + TimeInsOffset), InsLen, PAGE_EXECUTE_READWRITE, &old);
    BYTE INS[] = { 0x90,0x90,0x90,0x90,0x90,0x90 };
    memcpy((void *)(BaseAddr + TimeInsOffset), INS, InsLen);
    VirtualProtect((void*)(BaseAddr + TimeInsOffset), InsLen, old, &old);
}


void MineDlg::OnBnClickedButton2() // 恢复字节即可取消时间暂停
{
    // TODO: 在此添加控件通知处理程序代码
    auto BaseAddr = GetBaseAddr();
    DWORD TimeOffset = 0x579C;
    DWORD TimeInsOffset = 0x2FF5;
    DWORD InsLen = 6;
    DWORD old;
    VirtualProtect((void*)(BaseAddr + TimeInsOffset), InsLen, PAGE_EXECUTE_READWRITE, &old);
    BYTE INS[] = { 0xFF,0x05,0x9C,0x57,0x00,0x01 };
    memcpy((void*)(BaseAddr + TimeInsOffset), INS, 6);
    VirtualProtect((void*)(BaseAddr + TimeInsOffset), InsLen, old, &old);
}

测试

image.png

透视

经过上面动态调试我们得出结论:0x2F80函数是踩雷函数。

我们如果调用这个函数,是不是就能够实现透视了呢?

我们依旧采取线程回调的方式

void MineDlg::OnBnClickedButton3()
{
    // TODO: 在此添加控件通知处理程序代码
    DWORD ESPOffset = 0x2f80;
    DWORD FuncAddr = GetBaseAddr() + ESPOffset;
    // 创建不带参数的线程
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FuncAddr, NULL, 0, NULL);
}

测试

image.png

一键扫雷

跟透视差不多,只不过创建带参数的线程回调

void MineDlg::OnBnClickedButton4()
{
    // TODO: 在此添加控件通知处理程序代码
    DWORD ESPOffset = 0x347C;
    DWORD FuncAddr = GetBaseAddr() + ESPOffset;
    //创建带参数的线程
    struct { int a; } s = { 0 };
    CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)FuncAddr, &s, NULL, NULL);

}

测试

image.png

总结

通过这个小项目,对WIN游戏安全有初步的认识,并且加强对软件的逆向思维,增强动态调试的能力,找到软件关键的基地址,通过CE修改器,初步pojie软件,了解软件的状态,修改时间(时间暂停等等),理解几个重要的API,FindWindow获取句柄,WriteProcessMemory写入内存信息,LoadLibraryA加载指定 DLL 并返回模块句柄,GetProcAddress,获取指定 dll 的导出函数的地址,CreateThread 线程回调函数等等。多写,多做,多调,多实验,加油,互勉。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2047312.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

适配器模式, 修饰器模式 与 代理模式

这三种模式, 感觉非常类似, 都是把核心类包一层, 在外部做一些额外的事情, 我还没发现他们之间具体的区别, 有想法的同学, 可以评论或者私聊我 适配器模式 简介: 就是在目标类外面包一层, 用以适配其他的模块,兼容整个程序框架 举个例子: 比如运动员, 中国运动员参加法国奥运…

市域社会治理平台规划建设方案

1. 建设背景与市域治理定义 市域社会治理作为国家治理体系的重要组成部分&#xff0c;具有承上启下的枢纽作用。2019年&#xff0c;全国市域社会治理现代化工作会议提出了推进市域社会治理现代化的总体思路&#xff0c;强调以城带乡、以点带面&#xff0c;明确了市域治理的方向…

[项目]文海泛舟测试报告

目录 一、项目背景 二、项目功能 三、功能测试 1. 测试用例&#xff1a; 2. 实际测试的部分&#xff08;含截图&#xff09; 1. 正常登录 2. 文章列表页显示/登录用户信息显示 3. 文章详情页内容显示/文章作者信息显示 4. 编辑功能 1. 点击“更新博客”按钮前 2. 点击…

前端开发攻略---Vue实现图像裁剪功能,支持用户通过图形界面进行裁剪区域的调整,最终生成裁剪后的图像。

目录 1、演示 2、实现原理 3、实现功能 4、代码 1、演示 2、实现原理 这里有详细介绍&#xff1a; 前端开发攻略---图片裁剪上传的原理-CSDN博客 3、实现功能 上传图像&#xff1a; 用户选择文件后&#xff0c;changeFile 方法读取文件内容并将其转换为 Data URL&#xff0c…

Amesim中动力电池建模方法与原则简介

引言 新能源动力电池一维仿真与三维仿真的主要区别在与&#xff0c;一维仿真中无法在仿真中精准的得到各个点的温度变化&#xff0c;其仅为质量块的平均温度。而在新能源动力电池一维仿真中&#xff0c;旨在对动力电池的策略、充放电时间等进行验证。而无论是策略还是充放电时…

jmreport测试数据库出现 权限不足,此功能需要分配角色 解决方法

目录 前言1. 问题所示2. 原理分析3. 解决方法前言 关于jmreport的补充可看官网:jmreport上线安全配置 1. 问题所示 jmreport测试数据库出现,出现如下所示的问题:权限不足,此功能需要分配角色! 截图如下所示: 2. 原理分析 对于原理分析的Bug,代表当前用户没有足够的…

HDFS的编程

一、HDFS原理 HDFS(Hadoop Distributed File System)是hadoop生态系统的一个重要组成部分,是hadoop中的的存储组件,在整个Hadoop中的地位非同一般,是最基础的一部分,因为它涉及到数据存储,MapReduce等计算模型都要依赖于存储在HDFS中的数据。HDFS是一个分布式文件系统,…

20款必试AI工具:轻松搞定设计到协作

随着人工智能技术的发展&#xff0c;各种AI工具如雨后春笋般涌现&#xff0c;给我们的工作和生活带来了极大便利。 在AI工具的海洋中&#xff0c;哪一款才是你的真命天子&#xff1f; 众所周知&#xff0c;AI工具如雨后春笋般涌现&#xff0c;让人目不暇接。面对琳琅满目的选…

Oracle 字符串转多行(REGEXP_SUBSTR)

方案一&#xff1a; SQL 1.一个数据表(TABLE1_ZK)中存在一个字段(STRS)&#xff08;存储格式是以【,】隔开的字符串&#xff09; 2.现需要将其查分为多行数据&#xff08;每行为其中一个字符串&#xff09; 3.sql SELECT t.id,REGEXP_SUBSTR(t.STRS, [^,], 1, LEVEL) AS ma…

招聘|头部云厂商招 PG 核心骨干 DBA【上海】

我们的招聘专区又回来了&#xff01;&#x1f3c3; Bytebase 作为先进的数据库 DevOps 团队协同工具 &#x1f527;&#xff0c;用户群里汇聚了 &#x1f497; 业界优秀的 DBA&#xff0c;SRE&#xff0c;运维的同学们 &#x1f31f;。 上周用户群里有小伙伴发招聘信息 &…

【观察者模式】设计模式系列: 实现与最佳实践案例分析

文章目录 观察者模式深入解析&#xff1a;在Java中的实现与应用1. 引言1.1 观察者模式简介1.2 模式的重要性及其在现实世界的应用示例1.3 本文的目标和读者定位 2. 观察者模式的基本概念2.1 定义与原理2.2 UML类图和时序图2.3 核心原则2.4 使用场景 3. 观察者模式与其他模式的关…

【数据结构】Java实现链表

目录 链表的概念 链表的实现 链表的功能 框架搭建 功能实现 打印链表 获取数据数量 查询数据 插入数据 头插法 尾插法 指定位置插入 删除数据 删除一个数据 删除多个相同数据 删除链表 完整代码 链表的概念 链表是一种物理存储结构上非连续存储结构&#xff0…

nosql----redis三主三从集群部署

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【uniapp/uview1.x】解决在 u-popup 弹出层中使用 u-calendar 日历组件弹出方向出 bug 的问题

这个方法适用 uview 1.x 版本&#xff1b; 如果这个方法不适用可能是 uview 版本不一样&#xff0c;可以参考&#xff1a;https://github.com/dcloudio/uni-ui/issues/915 试试看 bug 的效果如图所示&#xff1a; 因为我为 popup 设置的方向为 top&#xff1a; <u-popup …

人工智能算法,图像识别技术;基于大语言模型的跨境商品识别与问答系统;图像识别

目录 一 .研究背景 二,大语言模型介绍 三,数据采集与预处理 商品识别算法 四. 跨境商品问答系统设计 五.需要源码联系 一 .研究背景 在当今全球化的背景下&#xff0c;跨境电商行业迅速发展&#xff0c;为消费者提供了更广泛的购物选择和更便利的购物方式。然而&#xf…

OLED屏幕制造工艺流程

OLED屏幕制造工艺流程是一个复杂且精细的过程&#xff0c;涉及多个关键步骤以确保最终的显示效果和性能。以下是OLED屏幕制造工艺流程的主要步骤&#xff1a; 1. 衬底制作与准备 材料选择&#xff1a;OLED器件需要一个透明的导电衬底&#xff0c;通常使用玻璃或塑料材料。 清…

集成RJ45网口网络变压器(网络隔离变压器)是如何影响网通设备的传输速率的。

华强盛电子导读RJ45连接器网口-199中间2643-0038 集成RJ45网口的网络变压器&#xff08;网络隔离变压器&#xff09;通常是指将网络变压器与RJ45连接器直接集成在一起的产品&#xff0c;这样的设计使得变压器可以直接安装在网络电缆的连接点上&#xff0c;而不需要额外的连接器…

【源码+文档+调试讲解】多媒体信息共享平台

摘 要 随着信息时代的来临&#xff0c;过去的武理多媒体信息共享管理方式缺点逐渐暴露&#xff0c;对过去的武理多媒体信息共享管理方式的缺点进行分析&#xff0c;采取计算机方式构建武理多媒体信息共享系统。本文通过阅读相关文献&#xff0c;研究国内外相关技术&#xff0c…

Unity | 游戏开发中的优化思维

目录 ​​​​​​一、优化三板斧&#xff1a; 第1步&#xff1a;定标准 第2步&#xff1a;重数据 第3步&#xff1a;严测试 二、流程和性能的优化 1.定标准&#xff1a; 2.重数据&#xff1a; 三、交互和表现的优化 1.卡顿和延迟 2.手感硬 四、沟通和学习 ​​​​…

C语言深度解析:static与extern关键字全指南

[大师C语言]合集&#xff3b;大师C语言(第一篇)&#xff3d;C语言栈溢出背后的秘密&#xff3b;大师C语言(第二十五篇)&#xff3d;C语言字符串探秘&#xff3b;大师C语言(第二篇)&#xff3d;C语言main函数背后的秘密&#xff3b;大师C语言(第二十六篇)&#xff3d;C语言结构体…