游戏安全入门-扫雷分析远程线程注入
前言
无论学习什么,首先,我们应该有个目标,那么入门windows游戏安全,脑海中浮现出来的一个游戏 – 扫雷,一款家喻户晓的游戏,虽然已经被大家分析的不能再透了,但是我觉得自己去分析一下还是极好的,把它作为一个小目标再好不过了。
我们编写一个妙妙小工具,工具要求实现以下功能:时间暂停、修改表情、透视、一键扫雷等等。
本文所用工具:
Cheat Engine、x32dbg(ollydbg)、Visual Studio 2019
扫雷游戏分析
游戏数据在内存中是地址,那么第一个任务,找内存地址
打开CE修改器
修改时间->时间暂停
计数器的时间是一个精确的值,所以我们通过精确数值扫描出来,游戏开始之前计数器上的数是0,所以我们扫描0。
时间在变化,选择介于什么数值之间再次扫描
可得 0x100579c — winmine.exe+579C
我们发现这个数据都是直接通过基址 + 固定偏移能直接得到的。
然后我们对数据去找 是什么改写了这个地址,得到一个指令和指针:
时间:0x100579c
修改表情 - 没啥用
修改表情这个功能怎么搞我觉得还是很容易想到的,这个按钮的作用是重新开始游戏,开始游戏,游戏胜利,游戏失败。
(表情的状态被分成了两个变量(4byte)来控制)
所以它是一种状态,所以我们通过0和1进行扫描,游戏进行状态输入1进行扫描,还原游戏之后输入0进行扫描。
首先是游戏进行状态,输入1进行扫描
再点击表情,将游戏还原,输入0开始扫描
如此反复进行扫描,得到表情的内存地址
0x1005164 – winmine.exe+5164
但是嘞,修改成2或者3,表情没有心得反应,所以控制游戏胜利和游戏失败的是其他的地址,我们知道,一般来说,一个功能的代码在内存中基本上都是连续的,(就像你修改一个游戏的血量,浏览血量内存块,你可以发现怒气,蓝量等内存地址)
所以,我们浏览内存
0x1005164-4 = 0x1005160
修改为3,发现出现了戴墨镜的表情(游戏胜利)
但是这个胜利知识一个状态,并不能说明扫雷完成.
表情:0x1005160与0x1005164
帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)
透视 - 显示雷区
思考游戏结束的时候会自动显示所有的雷,因此我们动态调试,看看在哪个函数调用之后会显示所有的雷
经过几次的动态调试之后发现:0x2F80函数是我们要找的结果。
一键扫雷
通过透视,我们玩一把游戏,使得游戏胜利(点完最后一个)
然后后两个函数,是破纪录跟英雄榜的函数
ret来到了这儿,游戏通关了,来到了这儿,可以知道,这个0x347c就是判断输赢的函数
并且通过调试发现由一个参数 0 1 来控制,所以跟透视差不多,带个参数线程回调就完了
编写妙妙小工具
怎么实现这个工具呢,当然是选择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 的选项中加入代码,让它在加载的时候调用执行。
那么我们的步骤是:
- 打开指定进程获得句柄
- 开辟远程进程的空间,分配可读可写段。
- 调用 WriteProcessMemory 将 dll 路径写入该内存区域。
- 创建远程线程,回调函数使用 LoadLibrary 加载指定 dll。
- 等待返回(loadLibrary返回)
- 释放空间
- 释放句柄
- 返回结果
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 加载指定 dllHANDLE 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 加载指定 dllHANDLE 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)的方式编写(简单),使用静态编译的方式
然后我们需要在资源窗体,新建一个 Dialog ,简单包装一个界面
这样我们在加载窗体的时候需要创建一个窗体类对象用它的 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 掉,就可以实现时间暂停
写两个按钮,创建下面的事件实现时间暂停开关。
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);
}
测试
透视
经过上面动态调试我们得出结论:0x2F80函数是踩雷函数。
我们如果调用这个函数,是不是就能够实现透视了呢?
我们依旧采取线程回调的方式
void MineDlg::OnBnClickedButton3()
{// TODO: 在此添加控件通知处理程序代码DWORD ESPOffset = 0x2f80;DWORD FuncAddr = GetBaseAddr() + ESPOffset;// 创建不带参数的线程CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FuncAddr, NULL, 0, NULL);
}
测试
一键扫雷
跟透视差不多,只不过创建带参数的线程回调
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);}
测试
总结
通过这个小项目,对WIN游戏安全有初步的认识,并且加强对软件的逆向思维,增强动态调试的能力,找到软件关键的基地址,通过CE修改器,初步pojie软件,了解软件的状态,修改时间(时间暂停等等),理解几个重要的API,FindWindow获取句柄,WriteProcessMemory写入内存信息,LoadLibraryA加载指定 DLL 并返回模块句柄,GetProcAddress,获取指定 dll 的导出函数的地址,CreateThread 线程回调函数等等。多写,多做,多调,多实验,加油,互勉。
相关文章:

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

bert-base-chinese模型的完整训练、推理和一些思考
前言 使用google-bert/bert-base-chinese模型进行中文文本分类任务,使用THUCNews中文数据集进行训练,训练完成后,可以导出模型,进行预测。 项目详细介绍和数据下载 数据集下载地址 Github完整代码 现记录训练过程中的一些感悟…...
JS基础5(JS的作用域和JS预解析)
JS的作用域 1. 全局作用域 全局作用域是在代码的任何地方都能访问到的最外层作用域。在浏览器环境下,全局作用域就是window对象,因此所有在全局作用域中声明的变量和函数都会成为window对象的属性和方法。 var globalVar "I am global"; …...
Doris 夺命 30 连问!(中)
导言 抱歉,作为从 S2 开始的骨灰级玩家看到 EDGUZI 官宣首发上线,兴奋之余忘了写文档 - -||,还望各位看官老爷见谅,这次错了,下次还敢 ^_^ 这是继上次的 30 问上篇的中篇,也是 10 个问题,有些…...

书生.浦江大模型实战训练营——(四)书生·浦语大模型全链路开源开放体系
最近在学习书生.浦江大模型实战训练营,所有课程都免费,以关卡的形式学习,也比较有意思,提供免费的算力实战,真的很不错(无广)!欢迎大家一起学习,打开LLM探索大门…...

SpringBoot 整合 RabbitMQ 实现延迟消息
一、业务场景说明 用于解决用户下单以后,订单超时如何取消订单的问题。 用户进行下单操作(会有锁定商品库存、使用优惠券、积分一系列的操作);生成订单,获取订单的id;获取到设置的订单超时时间࿰…...

Cilium:基于开源 eBPF 的网络、安全性和可观察性
基于 eBPF 的网络、安全性和可观察性 Cilium 是一种开源的云原生解决方案,它利用 Linux 内核中的 eBPF 技术来提供、保护和监控工作负载之间的网络连接。 什么是 eBPF? eBPF 是一项源自 Linux 内核的技术,允许沙盒程序在特权上下文&#x…...
Axios 详解与使用指南
Axios 详解与使用指南 1. Axios 简介 Axios 是一个基于 Promise 的 HTTP 客户端,能够在浏览器和 Node.js 环境中运行。它提供了一种简便的方式来执行 HTTP 请求,并支持多种请求方法,如 GET、POST、PUT、DELETE 等。Axios 的配置灵活&#x…...

深度学习 —— 个人学习笔记20(转置卷积、全卷积网络)
声明 本文章为个人学习使用,版面观感若有不适请谅解,文中知识仅代表个人观点,若出现错误,欢迎各位批评指正。 三十九、转置卷积 import torch from torch import nndef trans_conv(X, K):h, w K.shapeY torch.zeros((X.shape[…...
解决Mac系统Python3.12版本pip安装报错error: externally-managed-environment的问题
遇到的问题 在Mac安装了Python3.12.x版本(3.12.3、3.12.4)后,当尝试pip3 install xxx的时候,总是报错:error: externally-managed-environment error: externally-managed-environment This environment is external…...

lvm知识终结
、什么是 LVM LVM 是 Linux 下对磁盘分区进行管理的一种工具,适合管理大存储设备,并允许用户动态调整文件系统的大小 lvm 常用的命令 功能 PV 管理命令 VG 管理命令 LV 管理命令 scan 扫描 pvscan vgscan lvscan create 创建 pvcreate v…...

ESP32S3 IDF 对 16路输入输出芯片MCP23017做了个简单的测试
这次还是使用了idf老版本4.4.7,上次用了5.3,感觉不好用,官方的MCP23017芯片是英文版,真的很难读明白,可能是我英语水平不够吧。先看看每个寄存器的功能: IODIRA 和 IODIRB: 输入/输出方向寄存器 IPOLA 和 I…...
【技术前沿】Flux.1部署教程入门--Stable Diffusion团队最前沿、免费的开源AI图像生成器
项目简介 FLUX.1 是一种新的开源图像生成模型。它由 Stable Diffusion 背后的团队 Black Forest Labs 开发。 官网中有以下功能开源供大家参考: FLUX.1 擅长在图像中准确再现文字,因此非常适合需要清晰文字或短语的设计。无论是标牌、书籍封面还是品牌…...
Redis 的 STREAM 和 RocketMQ 是两种不同的消息队列和流处理解决方案,它们在设计理念、功能和用途上有显著区别。以下是它们的主要区别:
20240813 Redis 的 STREAM 和 RocketMQ 是两种不同的消息队列和流处理解决方案,它们在设计理念、功能和用途上有显著区别。以下是它们的主要区别:1. 使用 Redis 的 Sorted Set 数据结构连接到 Redis示例用法添加事件获取滑动窗口内的事件移除过期事件连接…...
Visual Studio Code安装与C/C++语言运行(上)
Visual Studio Code(VS Code)作为微软开发的一款轻量级但功能强大的源代码编辑器,广泛应用于各种编程语言的开发,包括C/C。以下将详细介绍VS Code的安装过程以及与C/C语言运行环境的配置。 一、Visual Studio Code的安装 1. 准备…...

探索数据可视化,数据看板在各行业中的应用
数据可视化是一种通过图形化手段将数据呈现出来的技术,它将复杂的数据和信息转化为易于理解的图表、地图、仪表盘等视觉元素,使得数据的模式、趋势和关系更加直观地展现出来。通过数据可视化,用户可以快速识别重要信息、发现潜在问题…...

haralyzer 半自动,一次性少量数据采集快捷方法
使用场景:半自动,一次性少量数据采集需求在工作中还是不少遇到的,无论使用模拟的方式,或者破解都不太划算。其实这种需求,使用半自动爬虫是最简单的。不需要考虑网站反爬虫的问题,因为你使用的就是真实的浏…...
mall-admin-web-master前端项目下载依赖失败解决
碰壁后的总结 pythone 环境 2.XX版本,切记不要3.0以上的。node 16.x不能太高 错误案例 npm ERR! code 1 npm ERR! path D:\workspace\springBootMall\mall-admin-web-master\node_modules\node-sass npm ERR! command failed npm ERR! command C:\windows\system…...
【07】JVM是怎么实现invokedynamic的
在Java中,方法调用会被编译为invokeStatic,invokeSpecial,invokVirtual以及invokeInterface四种指令。这些指令与包含目标方法类名、方法名以及方法描述符的符号引用捆绑,在实际运行之前,JVM根据这个符号引用链接到具体…...

使用API有效率地管理Dynadot域名,查看参与的拍卖列表
前言 Dynadot是通过ICANN认证的域名注册商,自2002年成立以来,服务于全球108个国家和地区的客户,为数以万计的客户提供简洁,优惠,安全的域名注册以及管理服务。 Dynadot平台操作教程索引(包括域名邮箱&…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

AI语音助手的Python实现
引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...