├── MEM_WRITE_WATCH.md ├── MEM_WRITE_WATCH_internals.c ├── README.md └── ghidra_scripts └── FindStructuresContainingFieldAtOffset.java /MEM_WRITE_WATCH.md: -------------------------------------------------------------------------------- 1 | # MEM_WRITE_WATCH 2 | 3 | [VirtualAlloc](https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc) has a `MEM_WRITE_WATCH` flag that allows you to track when pages are written to. MSDN has this to say about it: 4 | 5 | > **MEM_WRITE_WATCH** 0x00200000 6 | > 7 | > Causes the system to track pages that are written to in the allocated region. If you specify this value, you must also specify **MEM_RESERVE**. 8 | > 9 | > To retrieve the addresses of the pages that have been written to since the region was allocated or the write-tracking state was reset, call the [GetWriteWatch](https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-getwritewatch) function. To reset the write-tracking state, call GetWriteWatch or [ResetWriteWatch](https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-resetwritewatch). The write-tracking feature remains enabled for the memory region until the region is freed. 10 | 11 | Internally this is implemented as a kind of shadow-bitmap of page dirty bits, with some additional logic glued in between. You can read my reverse engineered source [here](MEM_WRITE_WATCH_internals.c). 12 | 13 | When you call `VirtualAlloc` it goes through to `NtAllocateVirtualMemory`, which then calls into `MiAllocateVirtualMemory` using an opaque struct that is prepared by `MiAllocateVirtualMemoryPrepare`. 14 | 15 | `MiAllocateVirtualMemory` calls into `MiReserveUserMemory`. This function checks if the `MEM_WRITE_WATCH` flag is set, and if so it calls `MiCreateWriteWatchView`. 16 | 17 | `MiCreateWriteWatchView` sets the `WriteWatch` flag on the calling process (i.e. `EPROCESS->Flags.WriteWatch = 1`) and allocates a bitmap on the virtual address descriptor (VAD, i.e. `_MMVAD` struct), via `MiCreateVadEventBitmap`, that is used to track pages in the VAD that have been written to. The bitmap size is one bit per page, and the page size is fetched via `MiGetVadMandatoryPageSize`. 18 | 19 | The bitmap is allocated using `MiAllocatePool`, using a tag value of "Mmww" (0x77776d4d). It is an `_RTL_BITMAP_EX` stored inside a `_MI_VAD_EVENT_BLOCK` struct, at `_MI_VAD_EVENT_BLOCK->BitMap`. It is attached to the VAD via `MiInsertVadEvent`. 20 | 21 | The `_MI_VAD_EVENT_BLOCK` struct can be fetched for an `_MMVAD` struct using the `MiLocateLockedVadEvent` function. 22 | 23 | The userland `GetWriteWatch` function calls into `NtGetWriteWatch`, which effectively just loops through the VADs for a given range, checking if they've got `MEM_WRITE_WATCH` set, then finding the event blocks for those VADs and extracting the page indices/addresses that were written. The actual function is kinda long and complicated because this process involves TLB flushes, management of working sets, and pagetable lock, so I haven't included it in the RE'd code. 24 | 25 | The userland `ResetWriteWatch` function calls into `NtResetWriteWatch`. This function finds the VAD associated with the address passed in, checks that the right flags are set, then flushes the dirty bits to the PFNs. 26 | 27 | The core of the write watch behaviour is handled by `MiCaptureWriteWatchDirtyBit`. This function is called by a number of PTE management functions. The primary one is `MiWsleFlush`, which handles flushing working set pages. It is also called in some circumstances when the PTE validity bit is reverted, when addresses are marked as no access, when AWE region protection flags are changed, and during forking (surprise! Windows does actually have fork support). 28 | 29 | -------------------------------------------------------------------------------- /MEM_WRITE_WATCH_internals.c: -------------------------------------------------------------------------------- 1 | /* 2 | ntoskrnl.exe (ntkrnlmp.exe) - 10.0.19041.804 (WinBuild.160101.0800) 3 | from Windows 10 Pro for Workstations (20H2 19042.804) 4 | hash: d0051bfdd4c3622d135b754a9df8b4f7a458e3072be8ac94e798b649758593bb 5 | reversed 2021-02-20 6 | */ 7 | 8 | 9 | /* MiCreateWriteWatchView creates a write watch view for a VAD. This is used with MEM_WRITE_WATCH on VirtualProtect. 10 | 11 | Sets up the VAD event bitmap and sets the WriteWatch flag on the process. 12 | 13 | Callers are MiReserveUserMemory (likely called as part of VirtualAlloc) and MiAllocateChildVads. */ 14 | 15 | NTSTATUS MiCreateWriteWatchView(_EPROCESS *Process,_MMVAD *Vad,size_t Size) 16 | { 17 | NTSTATUS status; 18 | size_t vadPageSize; 19 | 20 | vadPageSize = MiGetVadMandatoryPageSize(Vad); 21 | status = MiCreateVadEventBitmap(Process,(_MMVAD *)Vad,((Size - 1) + vadPageSize & ~(vadPageSize - 1)) / vadPageSize,4); 22 | if (-1 < status) { 23 | LOCK(); 24 | /* Process->Flags.WriteWatch = 1; */ 25 | Process->FlagsUnion = Process->FlagsUnion | 0x8000; 26 | status = 0; 27 | } 28 | return status; 29 | } 30 | 31 | 32 | 33 | NTSTATUS MiCreateVadEventBitmap(_EPROCESS *Process,_MMVAD *Vad,size_t NumberOfPages,ulong WaitReason) 34 | { 35 | NTSTATUS status; 36 | _MI_VAD_EVENT_BLOCK *vadEventBlock; 37 | SIZE_T bitmapSize; 38 | 39 | bitmapSize = ((ulonglong)((NumberOfPages & 0x3f) != 0) + 9 + (NumberOfPages >> 6)) * 8; 40 | vadEventBlock = (_MI_VAD_EVENT_BLOCK *)MiAllocatePool(0x40,bitmapSize,2004315469); 41 | if (vadEventBlock == (_MI_VAD_EVENT_BLOCK *)0x0) { 42 | status = 0xC000009A; 43 | } 44 | else { 45 | status = PsChargeProcessNonPagedPoolQuota(Process,bitmapSize); 46 | if (status < 0) { 47 | ExFreePool(vadEventBlock); 48 | } 49 | else { 50 | vadEventBlock->WaitReason = WaitReason; 51 | /* vadEventBlock->Bitmap.Buffer = ... */ 52 | *(_MI_VAD_EVENT_BLOCK **)&(vadEventBlock->field_0x8).field_0x8 = vadEventBlock + 1; 53 | /* vadEventBlock->Bitmap.SizeOfBitMap */ 54 | *(size_t *)&vadEventBlock->field_0x8 = NumberOfPages; 55 | MiInsertVadEvent(Vad,vadEventBlock,1); 56 | status = 0; 57 | } 58 | } 59 | return status; 60 | } 61 | 62 | 63 | 64 | /* This is fairly rough RE work; I'm not 100% sure on everything here. */ 65 | void MiInsertVadEvent(_MMVAD *Vad,_MI_VAD_EVENT_BLOCK *EventBlock,int UnknownFlag) 66 | { 67 | KIRQL irql; 68 | _KLOCK_ENTRY_u_48 *p_Var1; 69 | _KLOCK_ENTRY_u_48 *SpinLock; 70 | longlong in_GS_OFFSET; 71 | _ETHREAD *thread; 72 | 73 | if (UnknownFlag == 1) { 74 | SpinLock = (_KLOCK_ENTRY_u_48 *)&DAT_140c4f600; 75 | thread = *(_ETHREAD **)(*(longlong *)(in_GS_OFFSET + 0x188) + 0xb8); 76 | p_Var1 = &thread->LockEntries[0].field_0x30; 77 | if ((*(byte *)&thread->LockEntries[2].field_0x20.field_0x8 & 7) != 2) { 78 | SpinLock = &thread->LockEntries[2].field_0x30; 79 | } 80 | irql = ExAcquireSpinLockExclusive((PKSPIN_LOCK)SpinLock); 81 | *(undefined4 *)&SpinLock->field_0x4 = 0; 82 | } 83 | else { 84 | irql = '\x11'; 85 | p_Var1 = (_KLOCK_ENTRY_u_48 *)0x0; 86 | } 87 | EventBlock->Next = (Vad->Core).EventList; 88 | (Vad->Core).EventList = EventBlock; 89 | if (irql != '\x11') { 90 | MiUnlockWorkingSetExclusive(p_Var1); 91 | } 92 | return; 93 | } 94 | 95 | 96 | 97 | /* This function sets the page dirty bit in a VAD, for later use with GetWriteWatch. 98 | 99 | The write watch bitmap is created by MiCreateVadEventBitmap. 100 | 101 | This function is called by MiRevertValidPte, MiWsleFlush, MiMakeVaRangeNoAccess, MiMakeCombineCandidateClean, MiProtectAweRegion, and 102 | MiBuildForkPte. */ 103 | 104 | void MiCaptureWriteWatchDirtyBit(_EPROCESS *Process,ULONG_PTR Address,_MMVAD *Vad) 105 | { 106 | byte *dirtyPageData; 107 | _MI_VAD_EVENT_BLOCK *lockedVadEvent; 108 | SIZE_T vadMandatoryPageSize; 109 | _MMVAD *vad; 110 | ULONG_PTR pageAddressOffsetInVad; 111 | _MMVAD_FLAGS vadFlags; 112 | 113 | /* Process->FlagsUnion is an incorrect union reference from Ghidra's decompiler. Actually references Process->Flags 114 | (_EPROCESS+0x464) 115 | Bit 5 (flags&0x20) appears to be VmDeleted. 116 | Interestingly there is a WriteWatch flag in bit 15 (flags&0x80) but that isn't referred to here. */ 117 | if ((Process->FlagsUnion & 0x20) == 0) { 118 | if ((Vad == (_MMVAD *)0x0) && (Vad = MiLocateAddress((PVOID)Address), (_MMVAD *)Vad == (_MMVAD *)0x0)) { 119 | return; 120 | } 121 | /* 122 | (Vad->Core).u is an incorrect union reference from Ghidra's decompiler. Actually references Vad->Core.LongFlags 123 | I initially thought this referenced VadFlags in the same union, but the bitfield accesses didn't line up. 124 | 125 | LongFlags is a combination of MEM_* constants and PAGE_* constants that you'd pass to VirtualAlloc. 126 | 0x04 is PAGE_READWRITE, 0x300000 is MEM_WRITE_WATCH | MEM_TOP_DOWN 127 | */ 128 | vadFlags = *(_MMVAD_FLAGS *)&(((_MMVAD *)Vad)->Core).u; 129 | /* if ((vadFlags & PAGE_READWRITE == 0) && (vadFlags & (MEM_WRITE_WATCH | MEM_TOP_DOWN) == 0)) { ... 130 | 131 | Unsure why MEM_TOP_DOWN must be set here. This seems consistent with other parts of kernel code that are related to write 132 | watching. */ 133 | if ((((uint)vadFlags & 4) == 0) && (((uint)vadFlags & 0x300000) == 0x300000)) { 134 | vad = (_MMVAD *)Vad; 135 | lockedVadEvent = MiLocateLockedVadEvent(Vad,DelayExecution); 136 | vadMandatoryPageSize = MiGetVadMandatoryPageSize(vad); 137 | /* This appears to calculate the index of the page in the VAD for the given address. 138 | 139 | address>>0xc is equivalent to address/4096, which it then subtracts from the starting page address. 140 | starting page address is just StartingVpn | StartingVpnHigh<<32 */ 141 | pageAddressOffsetInVad = 142 | (Address >> 0xc) - (ulonglong)CONCAT14((((_MMVAD *)Vad)->Core).StartingVpnHigh,(((_MMVAD *)Vad)->Core).StartingVpn); 143 | MiLockVadCore(Vad,pageAddressOffsetInVad % vadMandatoryPageSize); 144 | /* _MI_VAD_EVENT_BLOCK+0x8 is a union containing a whole bunch of stuff, so it's not possible to know for sure what's being 145 | touched here. 146 | 147 | However, intuition says that _MI_VAD_EVENT_BLOCK.BitMap (and _RTL_BITMAP_EX) would make sense if we're storing dirty page bits, 148 | and _RTL_BITMAP_EX+0x8 is the bitmap buffer. 149 | 150 | This implies that the original code looks like this: 151 | 152 | byte* dirtyPageData = (byte*)lockedVadEvent->Bitmap.Buffer; 153 | int bitmapOffsetByte = pageIndex / 8; 154 | int bitmapOffsetBit = 1 << (pageIndex & 3); 155 | *(dirtyPageData + bitmapOffsetByte) |= bitmapOffsetBit; 156 | 157 | This is setting the dirty bit on the current page in the VAD bitmap. */ 158 | dirtyPageData = (byte *)(*(longlong *)&(lockedVadEvent->unlabelled8).field_0x8 + 159 | ((longlong)(pageAddressOffsetInVad / vadMandatoryPageSize) >> 3)); 160 | *dirtyPageData = *dirtyPageData | '\x01' << (pageAddressOffsetInVad / vadMandatoryPageSize & 7); 161 | MiUnlockVadCore(Vad,2); 162 | } 163 | } 164 | return; 165 | } 166 | 167 | 168 | 169 | _MI_VAD_EVENT_BLOCK * MiLocateLockedVadEvent(_MMVAD *Vad,_KWAIT_REASON waitReason) 170 | { 171 | _MI_VAD_EVENT_BLOCK *vadEventBlock; 172 | 173 | vadEventBlock = (_MI_VAD_EVENT_BLOCK *)(Vad->Core).EventList; 174 | while ((vadEventBlock != (_MI_VAD_EVENT_BLOCK *)0x0 && ((vadEventBlock->WaitReason & waitReason) == 0))) { 175 | vadEventBlock = vadEventBlock->Next; 176 | } 177 | return vadEventBlock; 178 | } 179 | 180 | 181 | 182 | void MiLocateVadEvent(_MMVAD *Vad,_KWAIT_REASON WaitReason) 183 | { 184 | MiLocateLockedVadEvent(Vad,WaitReason); 185 | return; 186 | } 187 | 188 | 189 | 190 | SIZE_T MiGetVadMandatoryPageSize(_MMVAD *Vad) 191 | { 192 | uint vadFlags; 193 | 194 | /* Ghidra incorrectly picks up the union instead of the flags field here. */ 195 | vadFlags = (Vad->Core).u; 196 | if ((*(ulonglong *)(&MiVadPageSizes + (ulonglong)(vadFlags >> 0x12 & 3) * 8) < 0x200) && ((vadFlags >> 0x16 & 1) == 0)) { 197 | return 1; 198 | } 199 | return *(ulonglong *)(&MiVadPageSizes + (ulonglong)(vadFlags >> 0x12 & 3) * 8); 200 | } 201 | 202 | 203 | 204 | uint MiLockVadCore(_MMVAD *Vad,ULONG_PTR PageAddress) 205 | { 206 | longlong lVar1; 207 | uint vadFlags; 208 | longlong in_GS_OFFSET; 209 | byte in_CR8; 210 | uint *pVadFlags; 211 | 212 | if (((KiIrqlFlags != 0) && ((KiIrqlFlags & 1) != 0)) && (in_CR8 < 0x10)) { 213 | /* Unsure what GS:[0x20] is but that huge offset is weird. */ 214 | lVar1 = *(longlong *)(*(longlong *)(in_GS_OFFSET + 0x20) + 0x84b8); 215 | *(uint *)(lVar1 + 0x14) = *(uint *)(lVar1 + 0x14) | (uint)(-1 << (in_CR8 + 1 & 0x3f)) & 4; 216 | } 217 | vadFlags = (Vad->Core).u; 218 | while( true ) { 219 | while ((vadFlags & 1) != 0) { 220 | if ((vadFlags & 2) == 0) { 221 | LOCK(); 222 | pVadFlags = &(Vad->Core).u; 223 | if (vadFlags == *pVadFlags) { 224 | *pVadFlags = vadFlags | 2; 225 | } 226 | else { 227 | vadFlags = *pVadFlags; 228 | } 229 | } 230 | else { 231 | do { 232 | KeYieldProcessorEx(); 233 | vadFlags = (Vad->Core).u; 234 | } while ((vadFlags & 1) != 0); 235 | } 236 | } 237 | LOCK(); 238 | pVadFlags = &(Vad->Core).u; 239 | if (vadFlags == *pVadFlags) break; 240 | vadFlags = *pVadFlags; 241 | } 242 | *pVadFlags = vadFlags & 0xfffffffd | 1; 243 | return vadFlags & 0xffffff00 | (uint)in_CR8; 244 | } 245 | 246 | 247 | 248 | uint MiUnlockVadCore(_MMVAD *Vad,byte param_2) 249 | { 250 | uint *puVar1; 251 | ulonglong uVar2; 252 | longlong lVar3; 253 | uint uVar4; 254 | uint uVar5; 255 | longlong in_GS_OFFSET; 256 | uint in_CR8; 257 | 258 | uVar4 = (Vad->Core).u; 259 | while( true ) { 260 | LOCK(); 261 | puVar1 = &(Vad->Core).u; 262 | if (uVar4 == *puVar1) break; 263 | uVar4 = *puVar1; 264 | } 265 | *puVar1 = uVar4 & 0xfffffffc; 266 | uVar4 = KiIrqlFlags; 267 | if ((((KiIrqlFlags != 0) && ((KiIrqlFlags & 1) != 0)) && (uVar4 = in_CR8, (byte)in_CR8 < 0x10)) && 268 | ((param_2 < 0x10 && (1 < (byte)in_CR8)))) { 269 | /* Again with this weird GS:[0x20]+0x84b8 offset. */ 270 | uVar2 = *(ulonglong *)(in_GS_OFFSET + 0x20); 271 | lVar3 = *(longlong *)(uVar2 + 0x84b8); 272 | uVar4 = ~((uint)(-1 << (param_2 + 1 & 0x3f)) & 0xffff); 273 | uVar5 = *(uint *)(lVar3 + 0x14) & uVar4; 274 | *(uint *)(lVar3 + 0x14) = uVar5; 275 | if (uVar5 == 0) { 276 | uVar4 = KiRemoveSystemWorkPriorityKick(uVar2); 277 | } 278 | } 279 | return uVar4; 280 | } 281 | 282 | 283 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Windows Reverse Engineering 2 | 3 | This repo contains a bunch of stuff I've reverse engineered from Windows, and some random Ghidra scripts that were helpful in the process. 4 | 5 | The code here is for reference purposes only, to help folks better understand how certain features behave, and save them some work if they want to do more research in the same areas. 6 | 7 | I may also add Ghidra databases or other machine-readable dumps of my RE work, but for now I'm focusing on publishing stuff as plaintext C because it shows up in search results that way. The reason I had to do this RE work is because the function names didn't show up when I tried searching. So now I've done the work so you don't have to :) -------------------------------------------------------------------------------- /ghidra_scripts/FindStructuresContainingFieldAtOffset.java: -------------------------------------------------------------------------------- 1 | //Finds structures that have a particular field at an offset, including in nested structures and unions. 2 | //@author Graham Sutherland 3 | //@category Search 4 | //@keybinding 5 | //@menupath 6 | //@toolbar 7 | 8 | import java.util.Stack; 9 | import java.util.List; 10 | import java.util.ListIterator; 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | import ghidra.app.script.GhidraScript; 14 | import ghidra.program.model.util.*; 15 | import ghidra.program.model.reloc.*; 16 | import ghidra.program.model.data.*; 17 | import ghidra.program.model.block.*; 18 | import ghidra.program.model.symbol.*; 19 | import ghidra.program.model.scalar.*; 20 | import ghidra.program.model.mem.*; 21 | import ghidra.program.model.listing.*; 22 | import ghidra.program.model.lang.*; 23 | import ghidra.program.model.pcode.*; 24 | import ghidra.program.model.address.*; 25 | import ghidra.program.model.address.Address; 26 | 27 | public class FindStructuresContainingFieldAtOffset extends GhidraScript 28 | { 29 | 30 | public class TypeInfo 31 | { 32 | public TypeInfo parent; 33 | public int offsetInParent; 34 | public int remainingOffset; 35 | public DataType struct; 36 | public Union union; 37 | public String name; 38 | public int depth; 39 | 40 | public TypeInfo(TypeInfo parent, int offsetInParent, int remainingOffset, DataType struct, Union union, String name) 41 | { 42 | this.parent = parent; 43 | this.offsetInParent = offsetInParent; 44 | this.remainingOffset = remainingOffset; 45 | this.struct = struct; 46 | this.union = union; 47 | this.name = name; 48 | this.depth = (parent == null) ? 0 : parent.depth + 1; 49 | } 50 | } 51 | 52 | private void printMatch(TypeInfo type, DataTypeComponent component) 53 | { 54 | TypeInfo baseTypeInfo = type; 55 | Stack typeStack = new Stack(); 56 | while (baseTypeInfo.parent != null) 57 | { 58 | typeStack.push(baseTypeInfo); 59 | baseTypeInfo = baseTypeInfo.parent; 60 | } 61 | String fullName = baseTypeInfo.struct.getDataTypePath().toString(); 62 | TypeInfo t = null; 63 | while(!typeStack.empty()) 64 | { 65 | t = typeStack.pop(); 66 | String name = t.name; //(t.name != null) ? t.name : t.struct.getDisplayName(); //(t.struct != null) ? t.struct.getDisplayName() : t.union.getDisplayName(); 67 | if (name.length() == 0) 68 | { 69 | name = "[unnamed@" + t.offsetInParent + "]"; 70 | } 71 | fullName += /*"[+" + t.offsetInParent + "]."*/ "." + name; 72 | } 73 | //fullName += " [" + ((t.struct != null) ? t.struct.getDisplayName() : t.union.getDisplayName()) + "]"; 74 | println(fullName); 75 | } 76 | 77 | /** 78 | * @see ghidra.app.script.GhidraScript#run() 79 | */ 80 | @Override 81 | public void run() throws Exception 82 | { 83 | if (currentProgram == null) 84 | { 85 | println("NO CURRENT PROGRAM"); 86 | return; 87 | } 88 | 89 | DataTypeManager dtm = currentProgram.getDataTypeManager(); 90 | int maxSize = askInt("Struct size", "Enter maximum struct size: "); 91 | int targetFieldOffset = askInt("Field offset", "Enter offset of field in struct: "); 92 | String targetFieldTypeName = askString("Struct size", "Enter field type name (search matches type names containing this string): "); 93 | 94 | // add all structs to type info collection 95 | List typesInfo = new ArrayList(); 96 | Iterator structures = dtm.getAllStructures(); 97 | while (structures.hasNext()) 98 | { 99 | Structure structure = structures.next(); 100 | typesInfo.add(new TypeInfo(null, 0, targetFieldOffset, structure, null, null)); 101 | } 102 | 103 | // iterate through types to find ones that match our expectations 104 | ListIterator typeInfoIterator = typesInfo.listIterator(); 105 | while (typeInfoIterator.hasNext()) 106 | { 107 | TypeInfo typeInfo = typeInfoIterator.next(); 108 | if (typeInfo.offsetInParent < 0 || typeInfo.remainingOffset < 0) 109 | { 110 | println("ERROR: NEGATIVE OFFSET!"); 111 | } 112 | 113 | // check if the type is nested (i.e. we started looking at a type and now we're looking at a field type) 114 | boolean isNested = false; 115 | if (typeInfo.parent != null) 116 | { 117 | isNested = true; 118 | } 119 | 120 | if (typeInfo.depth > 0) 121 | { 122 | //println("Depth: " + typeInfo.depth); 123 | } 124 | 125 | // get the struct or union length 126 | int fieldLength = typeInfo.struct != null ? typeInfo.struct.getLength() : typeInfo.union.getLength(); 127 | 128 | // if this is not a child field we're investigating, check if the size is within the bounds set as part of the search 129 | if (!isNested && fieldLength > maxSize) 130 | { 131 | //println("Skipping " + typeInfo.struct.getDisplayName() + " because it is too large (" + typeInfo.struct.getLength() + ")."); 132 | if (typeInfo.depth > 0) 133 | { 134 | println("ERROR: DEPTH > 0"); 135 | } 136 | continue; 137 | } 138 | 139 | int targetOffsetInType = typeInfo.remainingOffset; 140 | /*if (isNested) 141 | { 142 | targetOffsetInType = targetFieldOffset - typeInfo.remainingOffset; 143 | }*/ 144 | 145 | // if this is a struct, check through its fields 146 | if (typeInfo.struct != null && typeInfo.struct instanceof Structure) 147 | { 148 | Structure structure = (Structure)typeInfo.struct; 149 | 150 | /*boolean isKTHREAD = structure.getDisplayName().contains("_KTHREAD"); 151 | if (isKTHREAD) 152 | { 153 | println("Hit KTHREAD type: " + structure.getDataTypePath().toString()); 154 | }*/ 155 | 156 | // find a child component that matches the remaining offset 157 | DataTypeComponent componentAtOffset = structure.getComponentAt(targetOffsetInType); 158 | if (componentAtOffset != null) 159 | { 160 | if (componentAtOffset.getOffset() == targetOffsetInType) 161 | { 162 | // this field sits exactly at the offset we're looking for, so check if it's the right type 163 | if (componentAtOffset.getDataType().getDisplayName().toLowerCase().contains(targetFieldTypeName.toLowerCase())) 164 | { 165 | //println("targetOffsetInType=" + targetOffsetInType); 166 | String fieldName = componentAtOffset.getFieldName(); 167 | if (fieldName == null) 168 | { 169 | fieldName = "[unnamed_field@0x" + Integer.toHexString(componentAtOffset.getOffset()) + "]"; 170 | } 171 | TypeInfo match = new TypeInfo(typeInfo, componentAtOffset.getOffset(), targetOffsetInType - componentAtOffset.getOffset(), structure, null, fieldName); 172 | printMatch(match, componentAtOffset); 173 | } 174 | } 175 | 176 | /*if (componentAtOffset.getOffset() <= targetOffsetInType) 177 | {*/ 178 | // this field is either exactly at the offset we're looking for (and therefore might have a first child field that matches) or is before the the offset we're looking for 179 | 180 | DataType componentType = componentAtOffset.getDataType(); 181 | 182 | String fieldName = componentAtOffset.getFieldName(); 183 | if (fieldName == null) 184 | { 185 | fieldName = "[unnamed_field@0x" + Integer.toHexString(componentAtOffset.getOffset()) + "]"; 186 | } 187 | 188 | //String objectClass = (componentType instanceof Structure) ? "struct" : "union"; 189 | 190 | //println("Investgating " + objectClass + " " + fieldName + " at offset 0x" + Integer.toHexString(componentAtOffset.getOffset()) + " (remaining offset = 0x" + Integer.toHexString(targetOffsetInType - componentAtOffset.getOffset()) + ")"); 191 | 192 | // add to nested types to investigate 193 | 194 | if (componentType instanceof Structure) 195 | { 196 | typeInfoIterator.add(new TypeInfo(typeInfo, componentAtOffset.getOffset(), targetOffsetInType - componentAtOffset.getOffset(), (Structure)componentType, null, fieldName)); 197 | typeInfoIterator.previous(); 198 | } 199 | else if (componentType instanceof Union) 200 | { 201 | typeInfoIterator.add(new TypeInfo(typeInfo, componentAtOffset.getOffset(), targetOffsetInType - componentAtOffset.getOffset(), null, (Union)componentType, fieldName)); 202 | typeInfoIterator.previous(); 203 | } 204 | else if (componentType instanceof Composite) 205 | { 206 | println("WARNING: Unexpected type."); 207 | typeInfoIterator.add(new TypeInfo(typeInfo, componentAtOffset.getOffset(), targetOffsetInType - componentAtOffset.getOffset(), (Composite)componentType, null, fieldName)); 208 | typeInfoIterator.previous(); 209 | } 210 | //} 211 | } 212 | /*else if (isKTHREAD) 213 | { 214 | println("KTHREAD was ignored because it did not have a component at the required offset."); 215 | }*/ 216 | } 217 | 218 | // if this is a union, check to see if any of its types 219 | if (typeInfo.union != null) 220 | { 221 | //println("Processing union " + typeInfo.name); 222 | 223 | Union union = typeInfo.union; 224 | for (DataTypeComponent unionComponent : union.getComponents()) 225 | { 226 | if (targetOffsetInType == 0) 227 | { 228 | // this field sits exactly at the offset we're looking for, so check if it's the right type 229 | if (unionComponent.getDataType().getDisplayName().toLowerCase().contains(targetFieldTypeName.toLowerCase())) 230 | { 231 | DataType unionComponentType = unionComponent.getDataType(); 232 | 233 | String fieldName = unionComponent.getFieldName(); 234 | if (fieldName == null) 235 | { 236 | fieldName = "[unnamed_field@0x" + Integer.toHexString(unionComponent.getOffset()) + "]"; 237 | } 238 | 239 | TypeInfo match = null; 240 | if (unionComponentType instanceof Structure) 241 | { 242 | match = new TypeInfo(typeInfo, 0, 0, (Structure)unionComponentType, null, fieldName); 243 | } 244 | else if (unionComponentType instanceof Union) 245 | { 246 | match = new TypeInfo(typeInfo, 0, 0, null, (Union)unionComponentType, fieldName); 247 | } 248 | else if (unionComponentType instanceof Pointer) 249 | { 250 | match = new TypeInfo(typeInfo, 0, 0, (Pointer)unionComponentType, null, fieldName); 251 | } 252 | else if (unionComponentType instanceof Composite) 253 | { 254 | println("WARNING: Unexpected type."); 255 | match = new TypeInfo(typeInfo, 0, 0, unionComponentType, null, fieldName); 256 | //println("Found match with an instance type neither structure nor union!"); 257 | //println(unionComponentType.toString() + " - " + unionComponentType.getClass().toString()); 258 | } 259 | if (match != null) 260 | { 261 | printMatch(match, unionComponent); 262 | } 263 | } 264 | } 265 | if (unionComponent.getLength() > targetOffsetInType) 266 | { 267 | //println("Investigating union member " + unionComponent.getFieldName()); 268 | 269 | DataType componentType = unionComponent.getDataType(); 270 | 271 | String fieldName = unionComponent.getFieldName(); 272 | if (fieldName == null) 273 | { 274 | fieldName = "[unnamed_field@0x" + Integer.toHexString(unionComponent.getOffset()) + "]"; 275 | } 276 | 277 | if (componentType instanceof Structure) 278 | { 279 | typeInfoIterator.add(new TypeInfo(typeInfo, 0, typeInfo.remainingOffset, (Structure)componentType, null, fieldName)); 280 | typeInfoIterator.previous(); 281 | } 282 | else if (componentType instanceof Union) 283 | { 284 | 285 | typeInfoIterator.add(new TypeInfo(typeInfo, 0, typeInfo.remainingOffset, null, (Union)componentType, fieldName)); 286 | typeInfoIterator.previous(); 287 | } 288 | else if (componentType instanceof Composite) 289 | { 290 | println("WARNING: Unexpected type."); 291 | typeInfoIterator.add(new TypeInfo(typeInfo, 0, typeInfo.remainingOffset, (Composite)componentType, null, fieldName)); 292 | typeInfoIterator.previous(); 293 | } 294 | } 295 | else 296 | { 297 | //println("Skipping union member " + unionComponent.getFieldName() + " because it is too small (0x" + Integer.toHexString(unionComponent.getLength()) + " <= 0x" + Integer.toHexString(targetOffsetInType) + ")"); 298 | } 299 | } 300 | } 301 | } 302 | println("Done."); 303 | } 304 | } 305 | --------------------------------------------------------------------------------