/// <summary> /// 使用 clr!DebugDebugger::IsDebuggerAttached() 检测是否存在托管调试器。 /// 注意,此方法不能检测到非托管调试器(如OllyDbg,x64dbg)的存在。 /// </summary> /// <returns></returns> public static bool HasManagedDebugger() { byte[] opcodes; byte * pCodeStart; byte * pCodeCurrent; byte * pCodeEnd; ldasm_data ldasmData; bool is64Bit; InitializeManaged(); if (_isDebuggerAttached()) { // 此时肯定有托管调试器附加 return(true); } // 此时不能保证托管调试器未调试当前进程 if (_pIsDebuggerAttached[0] == 0x33 && _pIsDebuggerAttached[1] == 0xC0 && _pIsDebuggerAttached[2] == 0xC3) { // 这是dnSpy反反调试的特征 return(true); } // 有可能特征变了,进一步校验 opcodes = new byte[_isDebuggerAttachedLength]; pCodeStart = _pIsDebuggerAttached; pCodeCurrent = pCodeStart; pCodeEnd = _pIsDebuggerAttached + _isDebuggerAttachedLength; is64Bit = sizeof(void *) == 8; while (true) { uint length; length = Ldasm.ldasm(pCodeCurrent, &ldasmData, is64Bit); if ((ldasmData.flags & Ldasm.F_INVALID) != 0) { throw new NotSupportedException(); } CopyOpcode(&ldasmData, pCodeCurrent, opcodes, (uint)(pCodeCurrent - pCodeStart)); pCodeCurrent += length; if (pCodeCurrent == pCodeEnd) { break; } } // 复制Opcodes if (DynamicCrc32.Compute(opcodes) != _isDebuggerAttachedCrc32) { // 如果CRC32不相等,那说明CLR可能被Patch了 return(true); } return(false); }
private static void Initialize() { StringBuilder stringBuilder; byte[] clrFile; if (_isInitialized) { return; } switch (Environment.Version.Major) { case 2: _clrModuleHandle = GetModuleHandle("mscorwks.dll"); break; case 4: _clrModuleHandle = GetModuleHandle("clr.dll"); break; default: throw new NotSupportedException(); } if (_clrModuleHandle == null) { throw new InvalidOperationException(); } stringBuilder = new StringBuilder((int)MAX_PATH); if (!GetModuleFileName(_clrModuleHandle, stringBuilder, MAX_PATH)) { throw new InvalidOperationException(); } clrFile = File.ReadAllBytes(stringBuilder.ToString()); fixed(byte *pPEImage = clrFile) _clrPEHeaderCrc32Original = DynamicCrc32.Compute(CopyPEHeader(pPEImage)); _isInitialized = true; }
/// <summary> /// 检查CLR模块的PE头是否被修改 /// </summary> /// <returns>如果被修改,返回 <see langword="true"/></returns> public static bool VerifyClrPEHeader() { return(DynamicCrc32.Compute(CopyPEHeader(_clrModuleHandle)) != _clrPEHeaderCrc32Original); }
private static void InitializeManaged() { void * clrModuleHandle; StringBuilder stringBuilder; byte[] clrFile; if (_isManagedInitialized) { return; } switch (Environment.Version.Major) { case 2: _pIsDebuggerAttached = (byte *)typeof(Debugger).GetMethod("IsDebuggerAttached", BindingFlags.NonPublic | BindingFlags.Static).MethodHandle.GetFunctionPointer(); // 和.NET 4.x不一样,这个Debugger.IsAttached的get属性调用了IsDebuggerAttached(),而不是直通CLR内部。 clrModuleHandle = GetModuleHandle("mscorwks.dll"); break; case 4: _pIsDebuggerAttached = (byte *)typeof(Debugger).GetMethod("get_IsAttached").MethodHandle.GetFunctionPointer(); // Debugger.IsAttached的get属性是一个有[MethodImpl(MethodImplOptions.InternalCall)]特性的方法,意思是实现在CLR内部,而且没有任何stub,直接指向CLR内部。 // 通过x64dbg调试,可以知道Debugger.get_IsAttached()对应clr!DebugDebugger::IsDebuggerAttached()。 clrModuleHandle = GetModuleHandle("clr.dll"); break; default: throw new NotSupportedException(); } _isDebuggerAttached = (IsDebuggerAttachedDelegate)Marshal.GetDelegateForFunctionPointer((IntPtr)_pIsDebuggerAttached, typeof(IsDebuggerAttachedDelegate)); if (clrModuleHandle == null) { throw new InvalidOperationException(); } stringBuilder = new StringBuilder((int)MAX_PATH); if (!GetModuleFileName(clrModuleHandle, stringBuilder, MAX_PATH)) { throw new InvalidOperationException(); } clrFile = File.ReadAllBytes(stringBuilder.ToString()); // 读取CLR模块文件内容 fixed(byte *pPEImage = clrFile) { PEInfo peInfo; uint isDebuggerAttachedRva; uint isDebuggerAttachedFoa; byte * pCodeStart; byte * pCodeCurrent; ldasm_data ldasmData; bool is64Bit; byte[] opcodes; peInfo = new PEInfo(pPEImage); isDebuggerAttachedRva = (uint)(_pIsDebuggerAttached - (byte *)clrModuleHandle); isDebuggerAttachedFoa = peInfo.ToFOA(isDebuggerAttachedRva); pCodeStart = pPEImage + isDebuggerAttachedFoa; pCodeCurrent = pCodeStart; is64Bit = sizeof(void *) == 8; opcodes = new byte[0x200]; // 分配远大于实际函数大小的内存 while (true) { uint length; length = Ldasm.ldasm(pCodeCurrent, &ldasmData, is64Bit); if ((ldasmData.flags & Ldasm.F_INVALID) != 0) { throw new NotSupportedException(); } CopyOpcode(&ldasmData, pCodeCurrent, opcodes, (uint)(pCodeCurrent - pCodeStart)); if (*pCodeCurrent == 0xC3) { // 找到了第一个ret指令 pCodeCurrent += length; break; } pCodeCurrent += length; } // 复制Opcode直到出现第一个ret _isDebuggerAttachedLength = (uint)(pCodeCurrent - pCodeStart); fixed(byte *pOpcodes = opcodes) _isDebuggerAttachedCrc32 = DynamicCrc32.Compute(pOpcodes, _isDebuggerAttachedLength); } _isManagedInitialized = true; }