.net也能写内存挂
最近在研究.net的内存挂。
写了很久的c++,发现c#写出来的东西实在太香。
折腾c#外挂已经有很长时间了。都是用socket和c++配合。
这个模式其实蛮成功的,用rpc调用的方式加上c#的天生await 非常好写逻辑
类似这样
最近想换个口味。注入托管dll到非托管进程
这样做只是为了解决我目前遇见的一个问题。
在一个多线程的程序上逆向,我挂了很多钩子,导致我读写数据和储存我自己的数据
非常容易出现多线程冲突问题,换到.net里以后
lock 和Monitor 在.net里是同线程不互锁。这样能让我不容易出现互锁现象。
有一段时间正在烦恼那些卖驱动的,只有读写功能为什么还能实现很多功能。
在我的认知里,要调用游戏部分函数才能更方便自己做出更有用的功能
当然这里确实有些外挂是只读取角色顶点就能绘制的。
后来细想一下其实不需要调用功能通过写入代码的方式获取执行就可以了。
就是只要有读写就可以了。
游戏外挂无非就是 读写和调用。 调用是可以通过写来实现。
比如hook某个dx的函数。或者修改虚函数表的地址,然后jmp 到自己的函数里
达到获取执行权限。
好比挂钩了GetTickCount 这个API ,然后目标游戏不断的调用这个API
我们在这里插入自己的调用逻辑就可以了。
想明白了这个,于是我就干起了注入托管dll 到游戏进程里的勾当
当然这个托管dll实在是太大了(因为会用Costura.Fody把第三方库都打包在一起)
想法是这样,注入到游戏进程里以后申请内存空间,然后通过asm编译成bytes
然后写入到内存后,再去调用他就可以了
这里要安利一个asm的库
GitHub - icedland/iced: Blazing fast and correct x86/x64 disassembler, assembler, decoder, encoder for Rust, .NET, Java, Python, Lua
这个库很香
在c#里写asm长这样子,大概就是写好asm以后编译成bytes的过程
然后要介绍在c#内怎么完成thiscall
游戏大部分是thiscall 所以我在内存中申请一段asm
然后通过c#去调用这个段代码就可以了
asm 的作用就是把传入的参数 push到堆栈然后call
之后返回eax 这样就完整的跑通调用了。
用iced把asm生成好,然后通过c#的委托去调用这段asm代码即可
最后实现的效果类是这样
至于读写就更简单了。C#自带Marshal可以直接读写。
而且c#也支持不安全指针 直接 *(int*)(0x123456) = 100;
然后我们无限的包装自己的读写函数,比如byte float int string 之类的读写就可以
至于hook 也可以通过委托回调到自己的c#代码,hook在c#完成编译以后写入到目标地址
至此,完整的c#外挂需要的功能都实现了。
例子分3个工程
TestApp:测试工程模拟调用目标程序比如游戏
InjectionDLL:注入DLL,负责加载.net的dll,如果是远线程注入,就注入这个DLL即可,例子工程是主动loadlibrary
CShareLoadModule : c#的主要工作dll
附上源码一份
netInjection.rar
//以下是补充 2022 10 09
/
因为上面代码是用asm作为Thiscall 调用的,后来发现c#是自带调用约定的可以更优美的实现thiscall 调用
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_0(IntPtr ptr);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_1(IntPtr ptr, IntPtr p1);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_2(IntPtr ptr, IntPtr p1, IntPtr p2);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_3(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_4(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_5(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_6(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_7(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_8(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_9(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8, IntPtr p9);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_10(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8, IntPtr p9, IntPtr p10);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_11(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8, IntPtr p9, IntPtr p10, IntPtr p11);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_12(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8, IntPtr p9, IntPtr p10, IntPtr p11, IntPtr p12);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_13(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8, IntPtr p9, IntPtr p10, IntPtr p11, IntPtr p12, IntPtr p13);[UnmanagedFunctionPointer(CallingConvention.ThisCall)]public delegate IntPtr ThisCall_14(IntPtr ptr, IntPtr p1, IntPtr p2, IntPtr p3, IntPtr p4, IntPtr p5, IntPtr p6, IntPtr p7, IntPtr p8, IntPtr p9, IntPtr p10, IntPtr p11, IntPtr p12, IntPtr p13, IntPtr p14);unsafe public static IntPtr ThisCall(IntPtr address, IntPtr dwECX, params object[] args){IntPtr p1 = IntPtr.Zero;IntPtr[] paramPtr = new IntPtr[args.Length*2];int addParamCount = 0;for (int i = 0; i < args.Length; i++){var paramtype = args[i].GetType();if (paramtype == typeof(float)){var v = (float)args[i];paramPtr[addParamCount] = *(IntPtr*)&v;}else if (paramtype == typeof(double)){var v = (double)args[i];paramPtr[addParamCount] = *(IntPtr*)&v;addParamCount++;paramPtr[addParamCount] = *(IntPtr*)((&v) +4);}else if (paramtype == typeof(long)){var v = (long)args[i];paramPtr[addParamCount] = *(IntPtr*)&v;addParamCount++;paramPtr[addParamCount] = *(IntPtr*)((&v) + 4);}else if (paramtype == typeof(IntPtr)){var v = (IntPtr)args[i];paramPtr[addParamCount] = v;}else if (paramtype == typeof(PtrGameUIWindow)){paramPtr[addParamCount] = (args[i] as PtrGameUIWindow).ptr;}else{paramPtr[addParamCount] = (IntPtr)Convert.ToInt32(args[i]);}addParamCount++;}Log.Console($"ThisCall:0x{address.ToString("X8")} ECX:0x{dwECX.ToString("X8")} {paramPtr.ArrayToString(0, addParamCount)}");switch (args.Length){case 0:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_0>(address).Invoke(dwECX);break;case 1:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_1>(address).Invoke(dwECX, paramPtr[0]);break;case 2:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_2>(address).Invoke(dwECX, paramPtr[0], paramPtr[1]);break;case 3:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_3>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2]);break;case 4:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_4>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3]);break;case 5:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_5>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4]);break;case 6:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_6>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5]);break;case 7:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_7>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6]);break;case 8:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_8>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7]);break;case 9:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_9>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7], paramPtr[8]);break;case 10:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_10>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7], paramPtr[8], paramPtr[9]);break;case 11:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_11>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7], paramPtr[8], paramPtr[9], paramPtr[10]);break;case 12:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_12>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7], paramPtr[8], paramPtr[9], paramPtr[10], paramPtr[11]);break;case 13:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_13>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7], paramPtr[8], paramPtr[9], paramPtr[10], paramPtr[11], paramPtr[12]);break;case 14:p1 = Marshal.GetDelegateForFunctionPointer<ThisCall_14>(address).Invoke(dwECX, paramPtr[0], paramPtr[1], paramPtr[2], paramPtr[3], paramPtr[4], paramPtr[5], paramPtr[6], paramPtr[7], paramPtr[8], paramPtr[9], paramPtr[10], paramPtr[11], paramPtr[12], paramPtr[13]);break;default:Log.Error(new Exception("ThisCall 参数个数未预测"));p1 = IntPtr.Zero;break;}return p1;}
因为c#会定期GC的问题,导致c#自身函数可能会被GC修改函数位置
所以需要固定住代码位置新增一下函数固定Delegate
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]public delegate IntPtr Delegate_NewStringID2String(IntPtr dwESP);public uint LockDelegate(Delegate func){GCHandleList.Add(GCHandle.Alloc(func));GCHandleList.Add(GCHandle.Alloc(Marshal.GetFunctionPointerForDelegate(func), GCHandleType.Pinned));GCHandleList.Add(GCHandle.Alloc(func.Method.MethodHandle.GetFunctionPointer()));return (uint)Marshal.GetFunctionPointerForDelegate(func);}//使用方法public static IntPtr MyStringID2String(IntPtr dwESP){var ret = IntPtr.IntPtr.Zero;return ret;}public override void Start(){var asm = new Assembler(32);asm.pushad();asm.mov(eax, esp);asm.add(eax, 0x20);asm.push(eax);asm.mov(eax, LockDelegate(new Delegate_NewStringID2String(MyStringID2String)));asm.call(eax);asm.mov(__[esp + 0x1c], eax);asm.popad();asm.ret();var newJmpCode = PatchSelfHelper.AllocMem(0x30);PatchSelfHelper.WriteAssembler(newJmpCode, asm);}
相关文章:

.net也能写内存挂
最近在研究.net的内存挂。 写了很久的c,发现c#写出来的东西实在太香。 折腾c#外挂已经有很长时间了。都是用socket和c配合。 这个模式其实蛮成功的,用rpc调用的方式加上c#的天生await 非常好写逻辑 类似这样 最近想换个口味。注入托管dll到非托管进程 这样做只…...
python学习笔记2-数字转化为String
题目链接 str() 强制转换, sorted() 转换为有序列表,join() 将列表中的元素连接到字符串中,然后奇偶位组合成数字 class Solution:def splitNum(self, num: int) -> int:stnum "".join(sorted(str(num)))num1, num2 int(stn…...

MAC版Gradle构建Spring5.X源码阅读环境
前言: 三年前鄙人有幸在现已几乎报废的Window的DELL中搭建过Spring源码环境,今天,Mac版的搭建,来了。 本篇文章环境搭建:Spring5.2.1 Gradle5.6.3-all jdk8 IDEA2022.3版本 文章目录 1、Spring源码下载2、Gradle下载…...
Linux 常用通配符
通配符是一种特殊语句,主要有星号(*)和问号(?),用来模糊搜索文件。当查找文件夹时,可以使用它来代替一个或多个真正字符;当不知道真正字符或者懒得输入完整名字时&#x…...

Python皮卡丘
系列文章 序号文章目录直达链接1浪漫520表白代码https://want595.blog.csdn.net/article/details/1306668812满屏表白代码https://want595.blog.csdn.net/article/details/1297945183跳动的爱心https://want595.blog.csdn.net/article/details/1295031234漂浮爱心https://want…...
【数据结构与算法】三种简单排序算法,包括冒泡排序、选择排序、插入排序算法
冒泡排序算法 冒泡排序他是通过双重循环对每一个值进行比较,将小的值向后移动,以达到最终排序的结果,他的时间复杂度为O(n^2)。 /*** 冒泡排序* param arr*/public static void bubbleSort(int[] arr){int l arr.length;for (int i 0; i <…...

视频太大怎么压缩变小?超过1G的视频这样压缩
视频已经成为了我们日常生活中不可或缺的一部分,然而,很多时候,我们可能会遇到视频文件过大,无法在某些平台上传或保存的问题。那么,如何将过大的视频文件压缩变小呢? 下面就给大家分享三款实用的工具&…...

Edge 无法登录/同步问题【一招搞定】
目录 前言 一、打开 Edge 浏览器显示未同步,点击同步无效 二、Edge 登录报错 0x801901f4 或 0x80190001 解决方法 2.1 报错 0x801901f4 解决方法 2.1.0 Edge 登陆报错图示 2.1.1 添加 Edge 推荐的 DNS 地址 2.1.2 重新登录 Edge 账号成功 2.2 报错 0x801…...

ESP32-S3上手开发
1、搭建开发环境 首先搭建开发环境,这里采用了windows下集成开发环境ide进行开发,具体的安装方法:ESP-IDF安装配置 这里使用的乐鑫的esp32s3,N16R8 2、esp32s3模块 从上面图中可以看到,N16R8这里使用了外扩16M的fl…...

UE4和C++ 开发-编程基础记录(UE4+代码基础知识)
1、UE4基础元素 ①Actor 我们又见面了Actor,Actor是在一个关卡中持续存在的,通常他包含几个Actor组件。支持网络复制和多人游戏。 Actor不包含位置,方向。这些东西在Root Component中存储。对于UE3 中的Pawn也由PlayerCharacter继承了…...

【Unity】【VR】如何让Distance Grab抓取物品时限制物品的Rotation
【背景】 遇到这样的场景,希望抓取Canvas时,Canvas不会沿Z轴旋转。 【问题】 发现Freeze Canvas的Rigid Body没有用。 【分析】 应该是RigidBody的限制仅在物理互动下生效,抓取可能不属于物理互动(比如碰撞),所以不生效。 【思路】 还是得写脚本挂载在Interacta…...

为什么3ds max渲染效果图有噪点?点进来,CG Magic告诉您!
大家在使用3ds max渲染效果图时,可能渲染结果往往会出现的都是不真实,有小伙伴会问如何使3dmax渲染效果图真实呢? 不真实就算了,渲染过程中,会出现3Dmax渲染噪点多这类问题。 什么原因3ds max渲染效果图有噪点呢&a…...

Element UI怎么安装呢?
安装 :::warning 注意 后续演示将会在 Vue CLI 搭建的 Vue 项目上进行操作。如需要请查看 Vue CLI 安装 ::: 通过 YARN 命令安装 $ yarn add element-ui完整引入 代表一次性引入所有组件,比较省心省事,但是项目的打包体积也会跟着变大。 // main.js…...
redis批量删除命令
./redis-cli -h 127.0.0.1 -p 6379 -n 2 KEYS "170*:redisKeyStr" | xargs ./redis-cli -h 127.0.0.1 -p 6379 -n 2 DEL...

kubernetes环境 搭建
1、准备2台机器 2、安装docker环境(参考官网) 1、 sudo apt-get update sudo apt-get install ca-certificates curl gnupg2、 sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dea…...
TCP习题总结
1、在采用TCP连接的数据传输阶段,如果发送端的发送窗口值由1000变为2000,那么发送端在收到一个确认之前可以发送()。 A. 2000个TCP报文段 B. 2000B C. 1000B D. 1000 个 TCP 报文 这道题考察的是TCP的基本…...

华为发布LampSite X室内数字化创新解决方案,释放数字世界无限潜能
【阿联酋,迪拜,2023年10月11日】2023全球移动宽带论坛(Global MBB Forum 2022)期间,华为董事、ICT产品与解决方案总裁杨超斌重磅发布了全新一代5G室内数字化产品解决方案LampSite X系列,助力运营商打开商业…...
麒麟操作系统设置QT程序开机自启动有效方法
在麒麟操作系统上设置QT程序开机自启动的两种简单有效的方法。支持请点赞! 一、QT程序打包 1.设置环境变量 设置QT和linuxdeployqt的环境变量,已设置可忽略该步骤。 在/etc/profile文件末尾添加一下内容: export PATH/usr/local/Qt-5.15.…...

Python数组删除元素pop与remove对比
pop()和remove()函数都可以用来删除列表中的函数,pop()是按索引来删除的,remove()是按元素来删除的。 1、pop()默认删除列表中最后一个元素,而且会返回删除的元素。此时的时间复杂度为O(1) 下面的例子中,…...

【Java 进阶篇】Java Web 编写注册页面案例
当涉及到创建一个Java Web注册页面时,你将需要涵盖很多不同的主题,包括HTML、CSS、Java Servlet和数据库连接。在这篇文章中,我们将详细介绍每个步骤,以帮助你创建一个完整的注册页面。 1. 介绍 注册页面是许多Web应用程序的关键…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...