这篇文章的目的是介绍一款实验性的Uefi项目基于.NET NativeAOT模式实现的运行在Windows Hyper-V虚拟机上的windbg调试引擎开发心得。
internal unsafe struct DispatchCellInfo
{
public DispatchCellType CellType;
public MethodTable* InterfaceType;
public ushort InterfaceSlot;
public byte HasCache;
public uint MetadataToken;
public uint VTableOffset;
};
internal unsafe struct InterfaceDispatchCell
{
public IntPtr m_pStub;
public IntPtr **m_pCache**;
};
private unsafe static bool FindImplSlotInSimpleMap(MethodTable* pTgtType,
MethodTable* pItfType,
uint itfSlotNumber,
ushort* pImplSlotNumber,
MethodTable** ppGenericContext,
bool actuallyCheckVariance,
bool checkDefaultImplementations)
{
if (pTgtType->HasDispatchMap)
{
DispatchMap.DispatchMapEntry* i = fStaticDispatch ?
pMap->GetStaticEntry(checkDefaultImplementations ? (int)pMap->NumStandardStaticEntries : 0) :
pMap->GetEntry(checkDefaultImplementations ? (int)pMap->NumStandardEntries : 0);
DispatchMap.DispatchMapEntry* iEnd = fStaticDispatch ?
pMap->GetStaticEntry(checkDefaultImplementations ? (int)(pMap->NumStandardStaticEntries + pMap->NumDefaultStaticEntries) : (int)pMap->NumStandardStaticEntries) :
pMap->GetEntry(checkDefaultImplementations ? (int)(pMap->NumStandardEntries + pMap->NumDefaultEntries) : (int)pMap->NumStandardEntries);
for (; i != iEnd; i = fStaticDispatch ? (DispatchMap.DispatchMapEntry*)(((DispatchMap.StaticDispatchMapEntry*)i) + 1) : i + 1)
if (i->_usInterfaceMethodSlot == itfSlotNumber)
{
MethodTable* pCurEntryType = pTgtType->InterfaceMap[i->_usInterfaceIndex].InterfaceEntryType;
if (pCurEntryType == pItfType)
return true;
}
public unsafe static IntPtr FindInterfaceMethodImplementationTarget(MethodTable* pTgtType, MethodTable* pItfType, ushort itfSlotNumber, MethodTable** ppGenericContext)
{
if (FindImplSlotForCurrentType(
pCur, pItfType, itfSlotNumber, fDoDefaultImplementationLookup, &implSlotNumber, ppGenericContext))
{
if (implSlotNumber < pCur->NumVtableSlots)
{
//之后调用基类实现jmp rax
return pTgtType->GetVTableStartAddress()[(int)implSlotNumber];
}
}
private unsafe static IntPtr RhpCidResolve(object pObject, ref DispatchCellInfo cellInfo)
{
MethodTable* pInstanceType = pObject.GetMethodTable();
if (cellInfo.CellType == DispatchCellType.InterfaceAndSlot)
{
MethodTable* pResolvingInstanceType = pInstanceType;
IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, cellInfo.InterfaceType, cellInfo.InterfaceSlot, null);
return pTargetCode;
}
if (cellInfo.CellType == DispatchCellType.VTableOffset)
{
return *(IntPtr*)((byte*)pInstanceType + cellInfo.VTableOffset);
}
return IntPtr.Zero;
}
RhpInitialDynamicInterfaceDispatch PROC
sub rsp, 78h
mov [rsp+80h], rcx
mov [rsp+88h], rdx
mov [rsp+90h], r8
mov [rsp+98h], r9
movdqa xmmword ptr [rsp+20h], xmm0
movdqa xmmword ptr [rsp+30h], xmm1
movdqa xmmword ptr [rsp+40h], xmm2
movdqa xmmword ptr [rsp+50h], xmm3
mov rdx, r10
mov rcx, qword ptr [rsp+80h]
call RhpCidResolve
movdqa xmm0, xmmword ptr [rsp+20h]
movdqa xmm1, xmmword ptr [rsp+30h]
movdqa xmm2, xmmword ptr [rsp+40h]
movdqa xmm3, xmmword ptr [rsp+50h]
mov rcx, [rsp+80h]
mov rdx, [rsp+88h]
mov r8, [rsp+90h]
mov r9, [rsp+98h]
nop
add rsp, 78h
test rax,rax
je cleanup
jmp rax
cleanup:
ret
RhpInitialDynamicInterfaceDispatch ENDP
List<FunctionTraceMap> FunctionTokenMap = new List<FunctionTraceMap>();
IntPtr pMap =StartupCodeHelpers.GetModuleSectionWithLength(moduleSeg, ReadyToRunSectionType.BlobIdStackTraceMethodRvaToTokenMapping, ref length);
int* rvaToTokenMap = (int*)pMap;
int rvaToTokenMapEntryCount = (int)((int)length / (2 * sizeof(int)));
for (int entryIndex = 0; entryIndex < rvaToTokenMapEntryCount; entryIndex++)
{
int* pRelPtr32 = &rvaToTokenMap[2 * entryIndex + 0];
IntPtr pointer = (IntPtr)((IntPtr)pRelPtr32 + *pRelPtr32);
int methodRva = (int)(pointer - UefiApplication.ImageBase);
int token = rvaToTokenMap[2 * entryIndex + 1];
if (!methodRvaToTokenMap.ContainsKey(methodRva))
{
methodRvaToTokenMap.Add(methodRva, token);
}
}
IntPtr length = 0;
IntPtr pEmbeddedMetadata = StartupCodeHelpers.GetModuleSectionWithLength(moduleSeg, ReadyToRunSectionType.ReflectionMapBlobEmbeddedMetadata, ref length);
byte* pCurrent = (byte*)pEmbeddedMetadata;
byte* pCurrentsave = (byte*)pEmbeddedMetadata;
foreach (KeyValuePair<int, int> kv in methodRvaToTokenMap)
{
IntPtr methodRva =new IntPtr(kv.Key);
IntPtr metthodPtr = methodRva + UefiApplication.ImageBase;
int token = kv.Value & 0xffffff;
pCurrent = (byte*)pEmbeddedMetadata;
pCurrent += token;
UInt32 memberReference_parent = NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent);
UInt32 memberReference_name = NativePrimitiveDecoder.DecodeUnsigned(ref pCurrent);
IntPtr pCurrentstringsave = pEmbeddedMetadata + memberReference_name + 1;
//这个就是函数名
string methodname = string.FromASCII(pCurrentstringsave).Trim();
FunctionTraceMap traceMap = new FunctionTraceMap(metthodPtr, methodname);
FunctionTokenMap.Add(traceMap);
}
//获取某个函数的符号信息
private static bool DumpFunctionAddressAndName(IntPtr FunPtr)
{
int len = FunctionTokenMap.Count;
for (int j = 0; j < len; j++)
{
int k = j + 1;
FunctionTraceMap tmpj = FunctionTokenMap[j];
IntPtr FunPtrj = tmpj.FunctionAddress - modbase;
IntPtr FunDiff = FunPtr - tmpj.FunctionAddress;
FunctionTraceMap tmpk = FunctionTokenMap[k];
if (tmpk.FunctionAddress > FunPtr && FunPtr > tmpj.FunctionAddress - difflen)
{
Console.WriteLine(FunPtr.ToString("x") + ":=>" +":base+" + FunPtrj.ToString("x") + "=" + tmpj.FunctionName + "+" + FunDiff.ToString("x"));
}
}
EXTERN_C EFI_STATUS OutputStringWrapper(IN CHAR16* buf)
{
try {
ConsoleOutputString(buf);
}
catch (...)
{
//OutputStringWrapper$catch$0
Print(L"Exception OutputString Handler\r\n");
}
return 0;
}
kd> .fnent nt!OutputStringWrapper
Debugger function entry 00000145`3c7ce180 for:
[E:\git\UefiAotHyperV\WindbgUefiSharp\NativeUefi\nativelib.cpp @ 63] (00000000`f6744ac0) nt!OutputStringWrapper | (00000000`f6744ae0) nt!WriteLineWrapper
Exact matches:
nt!OutputStringWrapper (unsigned short *)BeginAddress = 00000000`00005ac0
EndAddress = 00000000`00005add
UnwindInfoAddress = 00000000`0002ebc8Unwind info at 00000000`f676dbc8, 10 bytes
version 1, flags 3, prolog 9, codes 1
handler routine: nt!__CxxFrameHandler4 (00000000`f6743360), data 2ebd8
00: offs 9, unwind op 2, op info 4 UWOP_ALLOC_SMALL.
typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
DWORD BeginAddress;
DWORD EndAddress;
union {
DWORD UnwindInfoAddress;
DWORD UnwindData;
} u;
} _IMAGE_RUNTIME_FUNCTION_ENTRY, * _PIMAGE_RUNTIME_FUNCTION_ENTRY;
typedef union _UNWIND_CODE {
struct {
UBYTE CodeOffset;
UBYTE UnwindOp : 4;
UBYTE OpInfo : 4;
};
USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
OPTIONAL ULONG ExceptionHandler;
* OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, *PUNWIND_INFO;
EXTERN_C UINT64 EFIAPI FindRuntimeFunction(UINT64 modbase,UINT64 fakefun)
{
FuncInfo4 FuncInfoDe;
UINT32 fakefunstart = (UINT64)fakefun - modbase; ;
for (PRUNTIME_FUNCTION RuntimeFunctionEntry = pImageRuntimeFunctionDirectory; RuntimeFunctionEntry < pImageRuntimeFunctionDirectoryEnd; RuntimeFunctionEntry++)
{
if (RuntimeFunctionEntry->BeginAddress + modbase <= fakefun && RuntimeFunctionEntry->EndAddress + modbase > fakefun)
{
PUNWIND_INFO UnwindDatainfo = (PUNWIND_INFO)(modbase + RuntimeFunctionEntry->u.UnwindInfoAddress);
PUNWIND_CODE UnwindCode = (PUNWIND_CODE)((UINT64)UnwindDatainfo + sizeof(UNWIND_INFO));
int len = ((UnwindDatainfo->CountOfCodes + 1) & ~1) + 2;
UINT32 ExceptionData = *(UINT32*)(UnwindCode + len);
uint8_t* buffer = (uint8_t*)modbase + ExceptionData;
DecompFuncInfo(buffer, FuncInfoDe, modbase, RuntimeFunctionEntry->BeginAddress);
TryBlockMap4 trymap(&FuncInfoDe, modbase);
HandlerMap4 handlermap(&trymap._tryBlock, modbase, RuntimeFunctionEntry->BeginAddress);
UINT64 realHandler = handlermap._handler.dispOfHandler + modbase;
实际上就是返回OutputStringWrapper$catch$地址
return (UINT64)realHandler;
}
}
return 0;
}
$psdir = Split-Path $MyInvocation.MyCommand.Path -Parent
$vhdxpath = Join-Path $psdir "hv.vhdx"
$uefishell = Join-Path $psdir "bootx64.efi"
$uefistartup = Join-Path $psdir "startup.nsh"
$uefiapp = Join-Path $psdir "windbg.efi"
$uefidir= "N:\EFI\BOOT\"
Write-Host $vhdxpath
$disk = New-VHD -Path $vhdxpath -Dynamic -SizeBytes 10GB | Mount-VHD -PassThru | Get-Disk
Initialize-Disk -Number $disk.Number -PartitionStyle GPT
New-Partition -DiskNumber $disk.Number -Size 500MB -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}" -DriveLetter "N"
Format-Volume -DriveLetter "N" -FileSystem FAT32 -Force -Confirm:$false
New-Item -ItemType "directory" -Path $uefidir
Copy-Item $uefishell -Destination $uefidir
Copy-Item $uefistartup -Destination $uefidir
Copy-Item $uefiapp -Destination "N:\"
Dismount-VHD -Path $vhdxpath
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -exec bypass -Command "Stop-VM -Name hv -TurnOff -Force"
"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\symstore.exe" add /r /f "E:\git\UefiAotHyperV\WindbgUefiSharp\Windbg\bin\Release\net7.0\win-x64\publish" /s "%SYMBOL_STORE%" /t niii
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -exec bypass -Command " Mount-VHD -Path 'F:\hyperv\hv\Virtual Hard Disks\hv.vhdx'"
copy "E:\git\UefiAotHyperV\WindbgUefiSharp\Windbg\bin\Release\net7.0\win-x64\native\Windbg.exe" "K:\Windbg.efi" /Y
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -exec bypass -Command "Dismount-VHD 'F:\hyperv\hv\Virtual Hard Disks\hv.vhdx'"
看雪ID:王cb
https://bbs.kanxue.com/user-home-609565.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多