人設(shè)生成器網(wǎng)站/怎么把平臺(tái)推廣出去
最近在研究.net的內(nèi)存掛。
寫了很久的c++,發(fā)現(xiàn)c#寫出來(lái)的東西實(shí)在太香。
折騰c#外掛已經(jīng)有很長(zhǎng)時(shí)間了。都是用socket和c++配合。
這個(gè)模式其實(shí)蠻成功的,用rpc調(diào)用的方式加上c#的天生await 非常好寫邏輯
類似這樣
最近想換個(gè)口味。注入托管dll到非托管進(jìn)程
這樣做只是為了解決我目前遇見(jiàn)的一個(gè)問(wèn)題。
在一個(gè)多線程的程序上逆向,我掛了很多鉤子,導(dǎo)致我讀寫數(shù)據(jù)和儲(chǔ)存我自己的數(shù)據(jù)
非常容易出現(xiàn)多線程沖突問(wèn)題,換到.net里以后
lock 和Monitor 在.net里是同線程不互鎖。這樣能讓我不容易出現(xiàn)互鎖現(xiàn)象。
有一段時(shí)間正在煩惱那些賣驅(qū)動(dòng)的,只有讀寫功能為什么還能實(shí)現(xiàn)很多功能。
在我的認(rèn)知里,要調(diào)用游戲部分函數(shù)才能更方便自己做出更有用的功能
當(dāng)然這里確實(shí)有些外掛是只讀取角色頂點(diǎn)就能繪制的。
后來(lái)細(xì)想一下其實(shí)不需要調(diào)用功能通過(guò)寫入代碼的方式獲取執(zhí)行就可以了。
就是只要有讀寫就可以了。
游戲外掛無(wú)非就是 讀寫和調(diào)用。 調(diào)用是可以通過(guò)寫來(lái)實(shí)現(xiàn)。
比如hook某個(gè)dx的函數(shù)。或者修改虛函數(shù)表的地址,然后jmp 到自己的函數(shù)里
達(dá)到獲取執(zhí)行權(quán)限。
好比掛鉤了GetTickCount? 這個(gè)API ,然后目標(biāo)游戲不斷的調(diào)用這個(gè)API
我們?cè)谶@里插入自己的調(diào)用邏輯就可以了。
想明白了這個(gè),于是我就干起了注入托管dll 到游戲進(jìn)程里的勾當(dāng)
當(dāng)然這個(gè)托管dll實(shí)在是太大了(因?yàn)闀?huì)用Costura.Fody把第三方庫(kù)都打包在一起)
想法是這樣,注入到游戲進(jìn)程里以后申請(qǐng)內(nèi)存空間,然后通過(guò)asm編譯成bytes
然后寫入到內(nèi)存后,再去調(diào)用他就可以了
這里要安利一個(gè)asm的庫(kù)?
GitHub - icedland/iced: Blazing fast and correct x86/x64 disassembler, assembler, decoder, encoder for Rust, .NET, Java, Python, Lua
這個(gè)庫(kù)很香
在c#里寫asm長(zhǎng)這樣子,大概就是寫好asm以后編譯成bytes的過(guò)程
然后要介紹在c#內(nèi)怎么完成thiscall
游戲大部分是thiscall? 所以我在內(nèi)存中申請(qǐng)一段asm
然后通過(guò)c#去調(diào)用這個(gè)段代碼就可以了
asm 的作用就是把傳入的參數(shù) push到堆棧然后call
之后返回eax 這樣就完整的跑通調(diào)用了。
用iced把a(bǔ)sm生成好,然后通過(guò)c#的委托去調(diào)用這段asm代碼即可
最后實(shí)現(xiàn)的效果類是這樣
至于讀寫就更簡(jiǎn)單了。C#自帶Marshal可以直接讀寫。
而且c#也支持不安全指針 直接 *(int*)(0x123456) = 100;
然后我們無(wú)限的包裝自己的讀寫函數(shù),比如byte float int string 之類的讀寫就可以
至于hook 也可以通過(guò)委托回調(diào)到自己的c#代碼,hook在c#完成編譯以后寫入到目標(biāo)地址
至此,完整的c#外掛需要的功能都實(shí)現(xiàn)了。
例子分3個(gè)工程
TestApp:測(cè)試工程模擬調(diào)用目標(biāo)程序比如游戲
InjectionDLL:注入DLL,負(fù)責(zé)加載.net的dll,如果是遠(yuǎn)線程注入,就注入這個(gè)DLL即可,例子工程是主動(dòng)loadlibrary
CShareLoadModule : c#的主要工作dll
附上源碼一份
netInjection.rar
//以下是補(bǔ)充 2022 10 09
/
因?yàn)樯厦娲a是用asm作為Thiscall 調(diào)用的,后來(lái)發(fā)現(xiàn)c#是自帶調(diào)用約定的可以更優(yōu)美的實(shí)現(xiàn)thiscall 調(diào)用
????????[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?參數(shù)個(gè)數(shù)未預(yù)測(cè)"));p1?=?IntPtr.Zero;break;}return?p1;}
因?yàn)閏#會(huì)定期GC的問(wèn)題,導(dǎo)致c#自身函數(shù)可能會(huì)被GC修改函數(shù)位置
所以需要固定住代碼位置新增一下函數(shù)固定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);}