最近有个需求,就是要在一个软件里面获取一个编号
当时我想这还不是so easy,找到那个框的句柄,发个消息就能拿到,百度一下
https://www.cnblogs.com/lujin49/p/4796502.html
几行代码不就搞定了,哈哈哈~
SPYXX掏出来,我擦!!!根本没有控件句柄!!!
怎么搞?再后来看了下软件,mmp是dotnet写的。。。。。。。
好吧,这招不行的话,我掏出CheatEngine就是一顿乱搜,好吧,找不到基址。。。
嘿嘿,.net写的软件不就是源码么,呵呵,掏出.net大杀器dnSpy,马上找到关键代码
private string xxx(SomeStruct ss)
{
Config.CyclePressValue = 25;
Config.TrgCycleTime = (DateTime.Now - this.TrgTime).TotalSeconds;
.
.
.
GC.Collect();
GC.WaitForPendingFinalizers();
return "";
}
SomeStruct那个结构体里面就有要找的那个数字,代码我修改了下,naked拿出来好像不太好。。。
这要是c写的就好办了,.net这种我以前还没搞过,好吧再百度找找有没有人整过这种类型的
欸,有了!!https://blog.csdn.net/xfgryujk/article/details/79053312
不过这只管hook到自己的代码,回不到原来的函数,而且还是clrcall类型的。。。
后来又搜了好多,基本没什么有用的信息,.net的hook资料是真的少~
好吧,那就自己来吧。既然JIT是运行时编译,那编译好的代码看看先吧,CE掏出一顿操作,CE还蛮强,.net的类和函数都能看到
找到上面的那个函数名称双击就能看到已经编译好的代码:
这里其实有个坑,等会说。。。
现在思路清晰了,俺们可以直接内存爆搜Opcode,找到JIT后的地址,然后直接hook这个地址
mov rax,我的代码地址 jmp rax
直接写个dll,然后注入就完事了。。。
这里有个问题,VS写64位asm代码不支持__asm{}这种类型了,要么你换intel的编译器,要么你写个.asm文件再asm+c混合编译。
下面重点经验来了:!!!
vs里面Cpp文件下,要引用asm中的函数,声明一下
extern "C" void asmcode();
asm文件中要引用cpp的函数,要这样写
extrn RunMyCode : proc
这样可以完美互相引用对方的函数
.asm文件中代码
extrn RunMyCode : proc .code asmcode proc push rbx push rcx push rdx push rbp push rsi push rdi push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15 pushfq //上面的代码保存现场,下面是运行hook后我们自己的代码,找到我们需要的那串数字 push rax lea rcx,[rdx+170h] call RunMyCode pop rax //恢复现场 popfq pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop r9 pop r8 pop rdi pop rsi pop rbp pop rdx pop rcx pop rbx //这里是被替换掉的原始代码,需要补上 push rdi push rsi push rbp push rbx sub rsp,28h mov rsi,rcx mov rdi,rdx //补完后可以跳回原来的函数地址 mov rax,1234567812345678h jmp rax asmcode endp end
CPP文件大概代码
#include "pch.h" #includeusing namespace std; static BYTE m_Shared[100] = { 0 }; extern "C" void RunMyCode(LPVOID pAddr) { //HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, L"MySharedMemory"); //if (hMap == NULL) //{ // hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"MySharedMemory"); // LPVOID pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); // ZeroMemory(pBuffer, 0x100); memcpy(m_Shared, pAddr, 10); // ::UnmapViewOfFile(pBuffer); // ::CloseHandle(hMap); //} } extern "C" void asmcode(); DWORD WINAPI InitHookThread(LPVOID dllMainThread) { WaitForSingleObject(dllMainThread, INFINITE); CloseHandle(dllMainThread); std::vector ResultArray; SearchMemory(GetCurrentProcess(), (char*)"57 56 55 53 48 83 EC 28 48 8B F1 48 8B FA C7 05 ?? ?? ?? ?? 19 00 00 00 E8 ?? ?? ?? ?? 48 8B C8 48 8B 56 50 E8 ?? ?? ?? ?? C4 E1 78 57 C0 C4 E1 FB 2A C0", 0x0, 0x7FFFFFFFFFFF, 10, ResultArray); if (ResultArray.size()>0) { LPVOID paddr = (LPVOID)ResultArray.at(0); __int64 dwFunc = GetFunAddress((PUCHAR)asmcode); for (PUCHAR pBuf = (PUCHAR)dwFunc; pBuf < ((PUCHAR)dwFunc + 1024); ++pBuf) { if (*(__int64*)pBuf == 0x1234567812345678) { __int64 tmp = (__int64)paddr + 0xE; DWORD dOldProtect; VirtualProtect((LPVOID)dwFunc,0x100, PAGE_EXECUTE_READWRITE, &dOldProtect); memcpy(pBuf, &tmp, sizeof(tmp)); break; } } BYTE jmpcode[] = { 0x48, 0xB8 , 0x78 , 0x56 , 0x34 , 0x12 , 0x78 , 0x56 , 0x34 , 0x12 , 0xFF , 0xE0 }; memcpy(jmpcode + 2, &dwFunc, 8); memcpy(paddr, jmpcode, sizeof(jmpcode)); } while (1) { Sleep(3000); } return 0; } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { //DisableThreadLibraryCalls(hModule); HANDLE curThread; if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &curThread, SYNCHRONIZE, FALSE, 0)) return FALSE; CloseHandle(CreateThread(NULL, 0, InitHookThread, curThread, 0, NULL)); } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
hook到那串数字后,本来打算用filemapping映射到内存来和自己的程序通信,后来省事直接在dll整了个static的内存,直接读dll的内存好了。
RunMyCode函数带一个参数,所以
lea rcx,[rdx+170h] ->rcx取到那串数字的偏移地址,作为参数
call RunMyCode
好,到这里注入后就OK成功了。。。
去现场一试,BOOM!!!其实到了反汇编这里,不BOOM才不正常。。。这就是上面说的坑!
后来调试发现,.net版本不一样,JIT后的OPCode显然会大大的不一样。。。包括结构体的偏移量,全都变~~
其实上面提到的那篇文章也说了,“hook .NET程序的难点在于.NET程序都是JIT临时编译的,函数的地址不确定。我翻了一下MSDN,发现可以用RuntimeMethodHandle的GetFunctionPointer获取编译后的函数地址,于是用C++/CLI调用.NET的反射和GetFunctionPointer就可以实现hook了”,但是整了那个clr问题好多,编译都编译不起来,只能这样了。。。
特此记录下hook .net遇到的坑~



