diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java new file mode 100644 index 0000000000..2cc5b2755b --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/datatype/microsoft/ThreadEnvironmentBlock.java @@ -0,0 +1,864 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.util.datatype.microsoft; + +import java.io.ByteArrayInputStream; +import java.math.BigInteger; + +import ghidra.app.cmd.register.SetRegisterCmd; +import ghidra.docking.settings.SettingsDefinition; +import ghidra.framework.store.LockException; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressOverflowException; +import ghidra.program.model.data.*; +import ghidra.program.model.lang.Register; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.*; +import ghidra.program.model.symbol.*; +import ghidra.program.model.util.CodeUnitInsertionException; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.exception.InvalidInputException; + +/** + * Class for creating a Ghidra memory block representing the TEB: Thread Environment Block. + * The class must be instantiated with the Program and the Windows OS version to control + * details of the TEB layout. The user must call setAddress to provide the starting address + * of the block to create. Then they must call one of + * - createBlockAndStructure or + * - createBlocksAndSymbols + * + * The TEB can be represented either by a single structure overlaying the + * block (createBlockAndStructure), or as a series of symbols and primitive + * data-types (createBlocksAndSymbols). + * + * Finally the user should call setRegisterValue. The TEB is accessed either through the FS segment + * (32-bit) or GS segment (64-bit), so this method sets a Register value for one these over + * the program. + */ +public class ThreadEnvironmentBlock { + + private Program program; + private int winVersion; // Ordered Windows version 3.10 => 30010, Win 20000 => 50000, XP => 50001, Vista => 60000 + private boolean is64Bit; + private Address tebAddress; // Where to map TEB in RAM + private int blockSize; + private DataType DWORDsize; + private DataType DWORD; + private DataType PVOID; + private DataType ULONGLONG; + private DataType ULONG; + private DataType LONG; + private DataType ULONG_PTR; + private DataType USHORT; + private DataType CHAR; + private DataType UCHAR; + private DataType WCHAR; + private DataType BOOLEAN; + private DataType HANDLE; + private DataType GUID; + private StructureDataType CLIENT_ID; + + public static final String BLOCK_NAME = "tdb"; + + /** + * An enumeration describing a Windows OS version by String and by ordinal. + * The most significant 1 or 2 digits of the ordinal specify the major Windows release. + * The least significant 4 digits provide the minor release. + */ + public static enum WinVersion { + WIN_3_10(30010, "Windows 3.10"), + WIN_3_50(30050, "Windows 3.50"), + WIN_95(40000, "Windows 95"), + WIN_2000(50000, "Windows 2000"), + WIN_XP(50001, "Windows XP"), + WIN_VISTA(60000, "Windows Vista"), + WIN_7(60001, "Windows 7"), + WIN_10(100000, "Windows 10"), + WIN_LATEST(101900, "Latest"); + + private String display; + private int order; + + WinVersion(int ord, String disp) { + display = disp; + order = ord; + } + + public int getOrder() { + return order; + } + + @Override + public String toString() { + return display; + } + } + + /** + * Class for creating specific fields of the TEB data structure + */ + private static abstract class LayDown { + protected boolean is64Bit; + + /** + * Create a single field given an offset, name, and data-type + * @param off32 is the offset for the 32-bit TEB (-1 means unused) + * @param off64 is the offset for the 64-bit TEB (-1 means unused) + * @param name is the name of the field + * @param dat is the data-type of the field + */ + public abstract void addEntry(int off32, int off64, String name, DataType dat); + + public LayDown(boolean is64) { + is64Bit = is64; + } + } + + /** + * Create TEB fields as components of a Structure data-type + */ + private static class LayDownStructure extends LayDown { + protected StructureDataType tebDataType; + + @Override + public void addEntry(int off32, int off64, String name, DataType dat) { + int offset = is64Bit ? off64 : off32; + if (offset < 0) { + return; + } + tebDataType.insertAtOffset(offset, dat, dat.getLength(), name, null); + } + + public LayDownStructure(boolean is64) { + super(is64); + tebDataType = new StructureDataType("TEB", 0); + } + } + + /** + * Create TEB fields as Symbols and CodeUnits on a MemoryBlock + */ + private static class LayDownFlat extends LayDown { + private Listing listing; + private DataTypeManager dtManager; + private SymbolTable symbolTable; + private Address baseAddr; + + @Override + public void addEntry(int off32, int off64, String name, DataType dat) { + int offset = is64Bit ? off64 : off32; + if (offset < 0) { + return; + } + Address addr = baseAddr.add(offset); + DataType clone = dat.clone(dtManager); + try { + listing.createData(addr, clone); + symbolTable.createLabel(addr, name, SourceType.ANALYSIS); + } + catch (CodeUnitInsertionException e) { + Msg.warn(this, "Unable to insert TEB field: " + name); + } + catch (DataTypeConflictException e) { + Msg.warn(this, "TEB data-type conflicts with existing data-type: " + dat.getName()); + } + catch (InvalidInputException e) { + Msg.warn(this, "Unable to create TEB symbol name: " + name); + } + } + + public LayDownFlat(Program program, Address addr, boolean is64) { + super(is64); + listing = program.getListing(); + dtManager = program.getDataTypeManager(); + symbolTable = program.getSymbolTable(); + baseAddr = addr; + } + } + + /** + * Set up (by name) the primitive data-types used by TEB fields. + */ + private void setupDataTypes() { + DWORDsize = is64Bit ? QWordDataType.dataType : DWordDataType.dataType; + DWORD = DWordDataType.dataType; // Always size 4 + PVOID = VoidDataType.dataType; + if (is64Bit) { + PVOID = new Pointer64DataType(PVOID); + ULONG_PTR = PVOID; + } + else { + PVOID = new Pointer32DataType(PVOID); + ULONG_PTR = PVOID; + } + ULONGLONG = UnsignedLongLongDataType.dataType; // Always size 8 + ULONG = DWordDataType.dataType; // Always size 4 + LONG = DWordDataType.dataType; + USHORT = UnsignedShortDataType.dataType; + UCHAR = UnsignedCharDataType.dataType; + WCHAR = WideChar16DataType.dataType; + CHAR = CharDataType.dataType; + BOOLEAN = BooleanDataType.dataType; + HANDLE = PVOID; + GUID = new GuidDataType(); + CLIENT_ID = new StructureDataType("CLIENT_ID", 0); + CLIENT_ID.add(HANDLE, HANDLE.getLength(), "UniqueProcess", null); + CLIENT_ID.add(HANDLE, HANDLE.getLength(), "UniqueThread", null); + } + + /** + * Create the TEB data structure by passing (offset, name, data-type) triples + * to the given field factory. The exact fields created is determined by + * the winVersion variable, specifying the Windows OS version. + * @param laydown is the given field factory + */ + private void create(LayDown laydown) { + + // TIB + laydown.addEntry(0, 0, "ExceptionList", PVOID); + laydown.addEntry(4, 8, "StackBase", PVOID); + laydown.addEntry(8, 0x10, "StackLimit", PVOID); + laydown.addEntry(0x0c, 0x18, "SubSystemTib", PVOID); + laydown.addEntry(0x10, 0x20, "FiberData", PVOID); + laydown.addEntry(0x14, 0x28, "ArbitraryUserPointer", PVOID); + laydown.addEntry(0x18, 0x30, "Self", PVOID); + + laydown.addEntry(0x1c, 0x38, "EnvironmentPointer", PVOID); + laydown.addEntry(0x20, 0x40, "ClientId", CLIENT_ID); + if (winVersion <= 30010) { + laydown.addEntry(0x28, 0x50, "pCSR_QLPC_TEB", PVOID); + laydown.addEntry(0x38, 0x6c, "unkbyte", DWORD); + } + else { + laydown.addEntry(0x28, 0x50, "ActiveRpcHandle", PVOID); + laydown.addEntry(0x38, 0x6c, "CountOfOwnedCriticalSections", ULONG); + } + laydown.addEntry(0x2c, 0x58, "ThreadLocalStoragePointer", PVOID); + laydown.addEntry(0x30, 0x60, "ProcessEnvironmentBlock", PVOID); + laydown.addEntry(0x34, 0x68, "LastErrorValue", ULONG); + + if (winVersion <= 30051) { + laydown.addEntry(0x3c, -1, "Win32ProcessInfo", PVOID); + } + else if (winVersion >= 40000) { + laydown.addEntry(0x3c, 0x70, "CsrClientThread", PVOID); + } + if (winVersion >= 30050) { + laydown.addEntry(0x40, 0x78, "Win32ThreadInfo", PVOID); // THREADINFO data-type + } + if (winVersion <= 30051) { + laydown.addEntry(0x44, -1, "CsrQlpcStack", PVOID); + } + if (winVersion == 40000) { + DataType dt = new ArrayDataType(ULONG, 0x1f, ULONG.getLength()); + laydown.addEntry(0x44, -1, "Win32ClientInfo", dt); + } + else if (winVersion >= 50000) { + DataType dt = new ArrayDataType(ULONG, 0x1a, ULONG.getLength()); + laydown.addEntry(0x44, 0x80, "User32Reserved", dt); + } + if (winVersion >= 50000) { + DataType dt = new ArrayDataType(ULONG, 5, ULONG.getLength()); + laydown.addEntry(0xac, 0xe8, "UserReserved", dt); + } + if (winVersion >= 40000) { + laydown.addEntry(0xc0, 0x100, "WOW32Reserved", PVOID); + } + laydown.addEntry(0xc4, 0x108, "CurrentLocale", ULONG); + laydown.addEntry(0xc8, 0x10c, "FpSoftwareStatusRegister", ULONG); + + if (winVersion >= 100000) { + DataType dt = new ArrayDataType(PVOID, 0x10, PVOID.getLength()); + laydown.addEntry(0xcc, 0x110, "ReservedForDebuggingInstrumentation", dt); + } + if (winVersion <= 60003) { + DataType dt = new ArrayDataType(PVOID, 0x36, PVOID.getLength()); + laydown.addEntry(0xcc, 0x110, "SystemReserved1", dt); + } + else if (winVersion <= 101511) { + DataType dt = new ArrayDataType(PVOID, 0x26, PVOID.getLength()); + laydown.addEntry(0x10c, 0x190, "SystemReserved1", dt); + } + else if (winVersion <= 101607) { + DataType dt = new ArrayDataType(PVOID, 0x24, PVOID.getLength()); + laydown.addEntry(0x10c, 0x190, "SystemReserved1", dt); + } + else if (winVersion <= 101703) { + DataType dt = new ArrayDataType(PVOID, 0x1e, PVOID.getLength()); + laydown.addEntry(0x10c, 0x190, "SystemReserved1", dt); + } + else { + DataType dt = new ArrayDataType(PVOID, 0x1a, PVOID.getLength()); + laydown.addEntry(0x10c, 0x190, "SystemReserved1", dt); + } + if (winVersion >= 101709) { + laydown.addEntry(0x174, 0x280, "PlaceholderCompatibilityMode", CHAR); + } + if (winVersion >= 101809) { + laydown.addEntry(0x175, 0x281, "PlaceholderHydrationAlwaysExplicit", BOOLEAN); + } + if (winVersion >= 101709 && winVersion <= 101803) { + DataType dt = new ArrayDataType(CHAR, 0x11, CHAR.getLength()); + laydown.addEntry(0x175, 0x281, "PlaceholderReserved", dt); + } + else if (winVersion >= 101809) { + DataType dt = new ArrayDataType(CHAR, 0x10, CHAR.getLength()); + laydown.addEntry(0x176, 0x282, "PlaceholderReserved", dt); + } + if (winVersion >= 101709) { + laydown.addEntry(0x180, 0x28c, "ProxiedProcessId", DWORD); + } + if (winVersion >= 101703) { + laydown.addEntry(0x184, 0x290, "ActivationStack", DWORDsize); // ACTIVATION_CONTEXT_STACK data-type + } + if (winVersion >= 101607) { + DataType dt = new ArrayDataType(UCHAR, 8, UCHAR.getLength()); + laydown.addEntry(0x19c, 0x2b8, "WorkingOnBehalfOfTicket", dt); + } + if (winVersion >= 30010 && winVersion <= 40000) { + laydown.addEntry(0x1a4, -1, "Spare1", PVOID); + } + if (winVersion >= 30010 && winVersion <= 30051) { + laydown.addEntry(0x1a8, -1, "Spare2", PVOID); + } + if (winVersion == 40000) { + laydown.addEntry(0x1a8, 0x2c0, "ExceptionCode", LONG); + } + else if (winVersion > 40000) { + laydown.addEntry(0x1a4, 0x2c0, "ExceptionCode", LONG); + } + if (winVersion > 60003) { + DataType dt = new ArrayDataType(UCHAR, 4, UCHAR.getLength()); + laydown.addEntry(-1, 0x2c4, "Padding0", dt); + } + if (winVersion == 30010) { + laydown.addEntry(0x1ac, -1, "Win32ThreadInfo", PVOID); + laydown.addEntry(0x1b0, -1, "Win32ProcessInfo", PVOID); + DataType dt = new ArrayDataType(HANDLE, 2, HANDLE.getLength()); + laydown.addEntry(0x1dc, -1, "DbgSsReserved", dt); + } + else if (winVersion >= 30050 && winVersion <= 30051) { + DataType dt = new ArrayDataType(PVOID, 5, PVOID.getLength()); + laydown.addEntry(0x1ac, -1, "CsrQlpcTeb", dt); + laydown.addEntry(0x1c0, -1, "Win32ClientInfo", dt); + } + if (winVersion >= 50001 && winVersion <= 50002) { + laydown.addEntry(0x1a8, -1, "ActivationContextStack", DWORDsize); // ACTIVATION_CONTEXT_STACK data-type + } + else if (winVersion > 50002) { + laydown.addEntry(0x1a8, -1, "ActivationContextStackPointer", PVOID); // Pointer to ACTIVATION_CONTEXT_STACK + } + if (winVersion >= 100000) { + laydown.addEntry(0x1ac, -1, "InstrumentationCallbackSp", ULONG_PTR); + laydown.addEntry(0x1b0, -1, "InstrumentationCallbackPreviousPc", ULONG_PTR); + laydown.addEntry(0x1b4, -1, "InstrumentationCallbackPreviousSp", ULONG_PTR); + laydown.addEntry(0x1b8, -1, "InstrumentationCallbackDisabled", BOOLEAN); + } + if (winVersion == 40000) { + DataType dt = new ArrayDataType(UCHAR, 0x28, UCHAR.getLength()); + laydown.addEntry(0x1ac, -1, "SpareBytes1", dt); + } + else if (winVersion == 50000) { + DataType dt = new ArrayDataType(UCHAR, 0x2c, UCHAR.getLength()); + laydown.addEntry(0x1a8, -1, "SpareBytes1", dt); + } + else if (winVersion >= 50001 && winVersion <= 50002) { + DataType dt = new ArrayDataType(UCHAR, 0x18, UCHAR.getLength()); + laydown.addEntry(0x1bc, -1, "SpareBytes1", dt); + } + else if (winVersion == 60000) { + DataType dt = new ArrayDataType(UCHAR, 0x24, UCHAR.getLength()); + laydown.addEntry(0x1ac, -1, "SpareBytes1", dt); + } + else if (winVersion >= 60001 && winVersion <= 60003) { + DataType dt = new ArrayDataType(UCHAR, 0x24, UCHAR.getLength()); + laydown.addEntry(0x1ac, -1, "SpareBytes", dt); + } + else if (winVersion >= 100000) { + DataType dt = new ArrayDataType(UCHAR, 0x17, UCHAR.getLength()); + laydown.addEntry(0x1b9, -1, "SpareBytes", dt); + } + if (winVersion >= 60000) { + laydown.addEntry(0x1d0, -1, "TxFsContext", ULONG); + } + if (winVersion > 50002) { + laydown.addEntry(-1, 0x2c8, "ActivationContextStackPointer", PVOID); // Pointer to ACTIVATION_CONTEXT_STACK + } + if (winVersion == 60000) { + DataType dt = new ArrayDataType(UCHAR, 0x18, UCHAR.getLength()); + laydown.addEntry(-1, 0x2d0, "SpareBytes1", dt); + } + else if (winVersion >= 60001 && winVersion <= 60003) { + DataType dt = new ArrayDataType(UCHAR, 0x18, UCHAR.getLength()); + laydown.addEntry(-1, 0x2d0, "SpareBytes", dt); + } + if (winVersion >= 100000) { + laydown.addEntry(-1, 0x2d0, "InstrumentationCallbackSp", ULONG_PTR); + laydown.addEntry(-1, 0x2d8, "InstrumentationCallbackPreviousPc", ULONG_PTR); + laydown.addEntry(-1, 0x2e0, "InstrumentationCallbackPreviousSp", ULONG_PTR); + } + if (winVersion >= 60000) { + laydown.addEntry(-1, 0x2e8, "TxFsContext", ULONG); + } + if (winVersion >= 100000) { + laydown.addEntry(-1, 0x2ec, "InstrumentationCallbackDisabled", BOOLEAN); + } + if (winVersion >= 101809) { + laydown.addEntry(-1, 0x2ed, "UnalignedLoadStoreExceptions", BOOLEAN); + } + if (winVersion == 60003) { + DataType dt = new ArrayDataType(UCHAR, 4, UCHAR.getLength()); + laydown.addEntry(-1, 0x2ec, "Padding1", dt); + } + else if (winVersion >= 100000 && winVersion <= 101803) { + DataType dt = new ArrayDataType(UCHAR, 3, UCHAR.getLength()); + laydown.addEntry(-1, 0x2ed, "Padding1", dt); + } + else { + DataType dt = new ArrayDataType(UCHAR, 2, UCHAR.getLength()); + laydown.addEntry(-1, 0x2ee, "Padding1", dt); + } + if (winVersion == 30010) { + DataType dt = new ArrayDataType(PVOID, 0x143, PVOID.getLength()); + laydown.addEntry(0x1e4, -1, "SystemReserved2", dt); + } + else if (winVersion >= 30050 && winVersion <= 30051) { + DataType dt = new ArrayDataType(PVOID, 0x142, PVOID.getLength()); + laydown.addEntry(0x1d4, -1, "SystemReserved2", dt); + } + else if (winVersion == 40000) { + DataType dt = new ArrayDataType(PVOID, 0xa, PVOID.getLength()); + laydown.addEntry(0x1d4, -1, "SystemReserved2", dt); + } + if (winVersion == 40000) { + laydown.addEntry(0x1fc, 0x2f0, "GdiTebBatch", DWORDsize); // GDI_TEB_BATCH data-type + } + else if (winVersion > 40000) { + laydown.addEntry(0x1d4, 0x2f0, "GdiTebBatch", DWORDsize); // GDI_TEB_BATCH data-type + } + if (winVersion >= 30050 && winVersion <= 40000) { + laydown.addEntry(0x6dc, -1, "gdiRgn", ULONG); + laydown.addEntry(0x6e0, -1, "gdiPen", ULONG); + laydown.addEntry(0x6e4, -1, "gdiBrush", ULONG); + } + if (winVersion >= 30050 && winVersion <= 40000) { + laydown.addEntry(0x6e8, 0x7d8, "RealClientId", CLIENT_ID); + } + else if (winVersion > 40000) { + laydown.addEntry(0x6b4, 0x7d8, "RealClientId", CLIENT_ID); + } + if (winVersion >= 30010 && winVersion < 30050) { + laydown.addEntry(0x6f0, 0x7e8, "CsrQlpcStack", PVOID); + } + else if (winVersion >= 30050 && winVersion <= 40000) { + laydown.addEntry(0x6f0, 0x7e8, "GdiCachedProcessHandle", PVOID); + } + else if (winVersion > 40000) { + laydown.addEntry(0x6bc, 0x7e8, "GdiCachedProcessHandle", PVOID); + } + if (winVersion >= 30010 && winVersion <= 40000) { + laydown.addEntry(0x6f4, 0x7f0, "GdiClientPID", ULONG); + laydown.addEntry(0x6f8, 0x7f4, "GdiCLientTID", ULONG); + laydown.addEntry(0x6fc, 0x7f8, "GdiThreadLocalInfo", PVOID); + } + else if (winVersion > 40000) { + laydown.addEntry(0x6c0, 0x7f0, "GdiClientPID", ULONG); + laydown.addEntry(0x6c4, 0x7f4, "GdiCLientTID", ULONG); + laydown.addEntry(0x6c8, 0x7f8, "GdiThreadLocalInfo", PVOID); + } + if (winVersion >= 30010 && winVersion <= 30051) { + laydown.addEntry(0x700, -1, "User32Reserved0", PVOID); + laydown.addEntry(0x704, -1, "User32Reserved1", PVOID); + } + if (winVersion == 30010) { + DataType dt = new ArrayDataType(PVOID, 0x13b, PVOID.getLength()); + laydown.addEntry(0x708, 0x800, "UserReserved", dt); + } + else if (winVersion == 30051) { + DataType dt = new ArrayDataType(PVOID, 3, PVOID.getLength()); + laydown.addEntry(0x708, 0x800, "UserReserved", dt); + } + else if (winVersion == 40000) { + DataType dt = new ArrayDataType(PVOID, 5, PVOID.getLength()); + laydown.addEntry(0x700, 0x800, "UserReserved", dt); + } + else if (winVersion >= 50000) { + DataType dt = new ArrayDataType(ULONG_PTR, 0x3e, ULONG_PTR.getLength()); + laydown.addEntry(0x6cc, 0x800, "Win32ClientInfo", dt); + } + if (winVersion == 30051) { + DataType dt = new ArrayDataType(PVOID, 0x133, PVOID.getLength()); + laydown.addEntry(0x714, 0x9f0, "glDispatchTable", dt); + } + else if (winVersion == 40000) { + DataType dt = new ArrayDataType(PVOID, 0x118, PVOID.getLength()); + laydown.addEntry(0x714, 0x9f0, "glDispatchTable", dt); + } + else if (winVersion > 40000) { + DataType dt = new ArrayDataType(PVOID, 0xe9, PVOID.getLength()); + laydown.addEntry(0x7c4, 0x9f0, "glDispatchTable", dt); + } + if (winVersion == 40000) { + DataType dt = new ArrayDataType(ULONG_PTR, 0x1a, ULONG_PTR.getLength()); + laydown.addEntry(0xb74, 0x1138, "glReserved1", dt); + } + else if (winVersion >= 50000) { + DataType dt = new ArrayDataType(ULONG_PTR, 0x1d, ULONG_PTR.getLength()); + laydown.addEntry(0xb68, 0x1138, "glReserved1", dt); + } + if (winVersion >= 40000) { + laydown.addEntry(0xbdc, 0x1220, "glReserved2", PVOID); + } + if (winVersion >= 30050) { + laydown.addEntry(0xbe0, 0x1228, "glSectionInfo", PVOID); + laydown.addEntry(0xbe4, 0x1230, "glSection", PVOID); + laydown.addEntry(0xbe8, 0x1238, "glTable", PVOID); + laydown.addEntry(0xbec, 0x1240, "glCurrentRC", PVOID); + laydown.addEntry(0xbf0, 0x1248, "glContext", PVOID); + } + laydown.addEntry(0xbf4, 0x1250, "LastStatusValue", ULONG); + if (winVersion >= 60003) { + DataType dt = new ArrayDataType(UCHAR, 4, UCHAR.getLength()); + laydown.addEntry(-1, 0x1254, "Padding2", dt); + } +// TODO: laydown.addEntry(0xbf8, 0x1258, "StaticUnicodeString", UNICODE_STRING); + DataType bufdt = new ArrayDataType(WCHAR, 0x105, WCHAR.getLength()); + laydown.addEntry(0xc00, 0x1268, "StaticUnicodeBuffer", bufdt); + if (winVersion >= 60003) { + DataType dt = new ArrayDataType(UCHAR, 6, UCHAR.getLength()); + laydown.addEntry(-1, 0x1472, "Padding3", dt); + } + laydown.addEntry(0xe0c, 0x1478, "DeallocationStack", PVOID); + DataType ptrdt = new ArrayDataType(PVOID, 0x40, PVOID.getLength()); + laydown.addEntry(0xe10, 0x1480, "TlsSlots", ptrdt); +// TODO: laydown.addEntry(0xf10, 0x1680, "TlsLinks", LIST_ENTRY); + laydown.addEntry(0xf10, 0x1680, "TlsLinks.Flink", PVOID); + laydown.addEntry(0xf14, 0x1688, "TlsLinks.Blink", PVOID); + laydown.addEntry(0xf18, 0x1690, "Vdm", PVOID); + laydown.addEntry(0xf1c, 0x1698, "ReservedForNtRpc", PVOID); + DataType handledt = new ArrayDataType(HANDLE, 2, HANDLE.getLength()); + laydown.addEntry(0xf20, 0x16a0, "DbgSsReserved", handledt); + if (winVersion >= 40000 && winVersion <= 50001) { + laydown.addEntry(0xf28, 0x16b0, "HardErrorsAreDisabled", ULONG); + } + else if (winVersion >= 50002) { + laydown.addEntry(0xf28, 0x16b0, "HardErrorMode", ULONG); + } + if (winVersion >= 60003) { + DataType dt = new ArrayDataType(UCHAR, 4, UCHAR.getLength()); + laydown.addEntry(-1, 0x16b4, "Padding4", dt); + } + if (winVersion >= 40000 && winVersion <= 50002) { + DataType dt = new ArrayDataType(PVOID, 0x10, PVOID.getLength()); + laydown.addEntry(0xf2c, 0x16b8, "Instrumentation", dt); + } + else if (winVersion >= 60000) { + DataType dt = new ArrayDataType(PVOID, is64Bit ? 0xb : 0x9, PVOID.getLength()); + laydown.addEntry(0xf2c, 0x16b8, "Instrumentation", dt); + } + if (winVersion >= 60000) { + laydown.addEntry(0xf50, 0x1710, "ActivityId", GUID); + } + if (winVersion > 50002) { + laydown.addEntry(0xf60, 0x1720, "SubProcessTag", PVOID); + } + if (winVersion >= 60000 && winVersion <= 60001) { + laydown.addEntry(0xf64, 0x1728, "EtwLocalData", PVOID); + } + else if (winVersion >= 60002) { + laydown.addEntry(0xf64, 0x1728, "PerflibData", PVOID); + } + if (winVersion > 50002) { + laydown.addEntry(0xf68, 0x1730, "EtwTraceData", PVOID); + } + if (winVersion >= 40000) { + laydown.addEntry(0xf6c, 0x1738, "WinSockData", PVOID); + laydown.addEntry(0xf70, 0x1740, "GdiBatchCount", ULONG); + } + if (winVersion >= 60001) { + laydown.addEntry(0xf74, 0x1744, "IdealProcessorValue", ULONG); + } + if (winVersion > 50002) { + laydown.addEntry(0xf78, 0x748, "GuaranteedStackBytes", ULONG); + } + if (winVersion >= 50000) { + laydown.addEntry(0xf7c, 0x1750, "ReservedForPerf", PVOID); + } + if (winVersion >= 40000) { + laydown.addEntry(0xf80, 0x1758, "ReservedForOle", PVOID); + laydown.addEntry(0xf84, 0x1760, "WaitingOnLoaderLock", ULONG); + } + if (winVersion >= 60000) { + laydown.addEntry(0xf88, 0x1768, "SavedPriorityState", PVOID); + } + if (winVersion >= 50002 && winVersion <= 60001) { + laydown.addEntry(0xf8c, 0x1770, "SoftPatchPtr1", ULONG_PTR); + } + else if (winVersion >= 60002) { + laydown.addEntry(0xf8c, 0x1770, "ReservedForCodeCoverage", ULONG_PTR); + } + if (winVersion >= 60000) { + laydown.addEntry(0xf90, 0x1778, "ThreadPoolData", PVOID); + } + if (winVersion >= 50000) { + laydown.addEntry(0xf94, 0x1780, "TlsExpansionSlots", PVOID); + } + if (winVersion > 50002) { + laydown.addEntry(-1, 0x1788, "DeallocationBStore", PVOID); + laydown.addEntry(-1, 0x1790, "BStoreLimit", PVOID); + } + if (winVersion >= 50000 && winVersion <= 60000) { + laydown.addEntry(0xf98, 0x1798, "ImpersonationLocale", ULONG); + } + else if (winVersion >= 60001) { + laydown.addEntry(0xf98, 0x1798, "MuiGeneration", ULONG); + } + if (winVersion >= 50000) { + laydown.addEntry(0xf9c, 0x179c, "IsImpersonating", ULONG); + laydown.addEntry(0xfa0, 0x17a0, "NlsCache", PVOID); + } + if (winVersion >= 50001) { + laydown.addEntry(0xfa4, 0x17a8, "pShimData", PVOID); + } + if (winVersion >= 50001 && winVersion <= 60001) { + laydown.addEntry(0xfa8, 0x17b0, "HeapVirtualAffinity", ULONG); + } + else if (winVersion >= 60002 && winVersion <= 101803) { + laydown.addEntry(0xfa8, 0x17b0, "HeapVirtualAffinity", USHORT); + } + else if (winVersion >= 101809) { + laydown.addEntry(0xfa8, 0x17b0, "HeapData", ULONG); + } + if (winVersion >= 60002 && winVersion <= 101803) { + laydown.addEntry(0xfaa, 0x17b2, "LowFragHeapDataSlot", USHORT); + } + if (winVersion >= 50001) { + laydown.addEntry(0xfac, 0x17b8, "CurrentTransactionHandle", PVOID); + laydown.addEntry(0xfb0, 0x17c0, "ActiveFrame", PVOID); // Pointer to TEB_ACTIVE_FRAME + } + if (winVersion >= 50002) { + laydown.addEntry(0xfb4, 0x17c8, "FlsData", PVOID); + } + if (winVersion >= 60000) { + laydown.addEntry(0xfb8, 0x17d0, "PreferredLanguages", PVOID); + laydown.addEntry(0xfbc, 0x17d8, "UserPrefLanguages", PVOID); + laydown.addEntry(0xfc0, 0x17e0, "MergedPrefLanguages", PVOID); + laydown.addEntry(0xfc4, 0x17e8, "MuiImpersonation", ULONG); + laydown.addEntry(0xfc8, 0x17ec, "CrossTebFlags", USHORT); + laydown.addEntry(0xfca, 0x17ee, "SameTebFlags", USHORT); + laydown.addEntry(0xfcc, 0x17f0, "TxnScopeEnterCallback", PVOID); + laydown.addEntry(0xfd0, 0x17f8, "TxnScopeExitCallback", PVOID); + laydown.addEntry(0xfd4, 0x1800, "TxnScopeContext", PVOID); + laydown.addEntry(0xfd8, 0x1808, "LockCount", ULONG); + } + if (winVersion >= 100000) { + laydown.addEntry(0xfdc, 0x180c, "WowTebOffset", LONG); + } + if (winVersion >= 60001) { + laydown.addEntry(0xfe0, 0x1810, "ResourceRetValue", PVOID); + } + if (winVersion >= 60002) { + laydown.addEntry(0xfe4, 0x1818, "ReservedForWdf", PVOID); + } + if (winVersion >= 100000) { + laydown.addEntry(0xfe8, 0x1820, "ReservedForCrt", ULONGLONG); + laydown.addEntry(0xff0, 0x1828, "EffectiveContainerId", GUID); + } + } + + /** + * Clear any Symbols or CodeUnits on the given block + * @param block is the specific MemoryBlock to clear + */ + private void clearBlock(MemoryBlock block) { + Listing listing = program.getListing(); + Address endAddr = tebAddress.add(blockSize - 1); + listing.clearCodeUnits(tebAddress, endAddr, false); + SymbolTable symbolTable = program.getSymbolTable(); + SymbolIterator iter = symbolTable.getSymbolIterator(tebAddress, true); + while (iter.hasNext()) { + Symbol sym = iter.next(); + if (!block.contains(sym.getAddress())) { + break; + } + sym.delete(); + } + } + + /** + * Create TEB as a single uninitialized block. A TEB structure is created and is + * placed on the block. + * @throws MemoryConflictException if there are overlap problems with other blocks + * @throws AddressOverflowException for problems with block's start Address + * @throws IllegalArgumentException for problems with the block name or the TEB data-type + * @throws LockException if it cannot get an exclusive lock on the program + * @throws DataTypeConflictException for conflicts with other data-types + * @throws CodeUnitInsertionException for problems laying down the structure on the block + * @throws InvalidInputException for problems with the symbol name attached to the TEB + */ + public void createBlockAndStructure() throws MemoryConflictException, LockException, + IllegalArgumentException, AddressOverflowException, CodeUnitInsertionException, + DataTypeConflictException, InvalidInputException { + Memory memory = program.getMemory(); + MemoryBlock block = memory.getBlock(BLOCK_NAME); + if (block != null) { + if (!block.getStart().equals(tebAddress) || block.getSize() != blockSize || + block.isInitialized()) { + throw new MemoryConflictException("Incompatible " + BLOCK_NAME + " block exists"); + } + clearBlock(block); + } + else { + block = memory.createUninitializedBlock(BLOCK_NAME, tebAddress, blockSize, false); + } + block.setWrite(true); + LayDownStructure laydown = new LayDownStructure(is64Bit); + create(laydown); + if (is64Bit) { + DataType selfRef = new Pointer64DataType(laydown.tebDataType); + laydown.tebDataType.replaceAtOffset(0x30, selfRef, 8, "Self", null); + } + else { + DataType selfRef = new Pointer32DataType(laydown.tebDataType); + laydown.tebDataType.replaceAtOffset(0x18, selfRef, 4, "Self", null); + } + Listing listing = program.getListing(); + listing.createData(tebAddress, laydown.tebDataType); + SymbolTable symbolTable = program.getSymbolTable(); + symbolTable.createLabel(tebAddress, "TEB", SourceType.ANALYSIS); + } + + /** + * Get the bytes for the TEB. These are all initialized to zero except for + * the "Self" field, which is set so that it contains the starting address + * of the TEB, allowing the ConstantPropagator to pick it up. + * @return the bytes as an InputStream + */ + private ByteArrayInputStream getTEBBytes() { + byte buffer[] = new byte[blockSize]; + for (int i = 0; i < blockSize; ++i) { + buffer[i] = 0; + } + long offset = tebAddress.getOffset(); + if (is64Bit) { + for (int i = 0; i < 8; ++i) { + buffer[0x30 + i] = (byte) offset; + offset >>= 8; + } + } + else { + for (int i = 0; i < 4; ++i) { + buffer[0x18 + i] = (byte) offset; + offset >>= 8; + } + } + return new ByteArrayInputStream(buffer); + } + + /** + * Mark a specific Data element as read-only + * @param data is the given Data element + */ + private void markDataAsConstant(Data data) { + SettingsDefinition[] settings = data.getDataType().getSettingsDefinitions(); + for (SettingsDefinition definitions : settings) { + if (definitions instanceof MutabilitySettingsDefinition) { + MutabilitySettingsDefinition setting = (MutabilitySettingsDefinition) definitions; + setting.setChoice(data, MutabilitySettingsDefinition.CONSTANT); + } + } + } + + /** + * Create 2 blocks, one that is initialized to hold a proper value for the TEB Self reference field + * and another to hold the remainder of the TEB. The data structure is layed down as a + * series of symbols on these blocks. + * @throws MemoryConflictException if there are overlap problems with other blocks + * @throws CancelledException if block creation is cancelled + * @throws AddressOverflowException for problems with block's start Address + * @throws IllegalArgumentException for problems with the block name or the TEB data-type + * @throws LockException if it cannot get an exclusive lock on the program + */ + public void createBlocksAndSymbols() throws MemoryConflictException, LockException, + IllegalArgumentException, AddressOverflowException, CancelledException { + Memory memory = program.getMemory(); + MemoryBlock block1 = memory.getBlock(BLOCK_NAME); + if (block1 != null) { + if (!block1.getStart().equals(tebAddress) || block1.getSize() != blockSize || + !block1.isInitialized()) { + throw new MemoryConflictException("Incompatible " + BLOCK_NAME + " block exists"); + } + clearBlock(block1); + } + else { + ByteArrayInputStream byteStream = getTEBBytes(); + block1 = memory.createInitializedBlock(BLOCK_NAME, tebAddress, byteStream, blockSize, + null, false); + } + block1.setWrite(true); + LayDownFlat laydown = new LayDownFlat(program, tebAddress, is64Bit); + create(laydown); + Data data = program.getListing().getDataAt(tebAddress.add(is64Bit ? 0x30 : 0x18)); + markDataAsConstant(data); + } + + /** + * Set FS_OFFSET for 32-bit or GS_OFFSET for 64-bit to the address of the TEB across the program. + */ + public void setRegisterValue() { + Register reg = program.getRegister(is64Bit ? "GS_OFFSET" : "FS_OFFSET"); + BigInteger val = BigInteger.valueOf(tebAddress.getOffset()); + for (MemoryBlock block : program.getMemory().getBlocks()) { + if (!block.isExecute()) { + continue; + } + SetRegisterCmd cmd = new SetRegisterCmd(reg, block.getStart(), block.getEnd(), val); + cmd.applyTo(program); + } + } + + /** + * @return true if a 64-bit TEB is being layed down. + */ + public boolean is64() { + return is64Bit; + } + + /** + * @return the number of bytes needed in the full TEB block being constructed + */ + public int getBlockSize() { + return blockSize; + } + + /** + * Set the starting address of the TEB + * @param addr is the Address to set + */ + public void setAddress(Address addr) { + tebAddress = addr; + } + + public ThreadEnvironmentBlock(Program prog, WinVersion version) { + program = prog; + tebAddress = null; + winVersion = version.getOrder(); + is64Bit = program.getLanguageID().getIdAsString().contains("64"); + blockSize = is64Bit ? 0x1850 : 0x1000; + setupDataTypes(); + } +} diff --git a/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/TEBAnalyzer.java b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/TEBAnalyzer.java new file mode 100644 index 0000000000..7e37211e67 --- /dev/null +++ b/Ghidra/Features/MicrosoftCodeAnalyzer/src/main/java/ghidra/app/plugin/prototype/MicrosoftCodeAnalyzerPlugin/TEBAnalyzer.java @@ -0,0 +1,139 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin; + +import ghidra.app.services.*; +import ghidra.app.util.datatype.microsoft.ThreadEnvironmentBlock; +import ghidra.app.util.datatype.microsoft.ThreadEnvironmentBlock.WinVersion; +import ghidra.app.util.importer.MessageLog; +import ghidra.framework.options.Options; +import ghidra.program.model.address.*; +import ghidra.program.model.listing.Program; +import ghidra.program.model.mem.Memory; +import ghidra.program.model.mem.MemoryBlock; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; +import ghidra.util.task.TaskMonitor; + +public class TEBAnalyzer extends AbstractAnalyzer { + private static final String NAME = "Windows x86 Thread Environment Block (TEB) Analyzer"; + private static final String DESCRIPTION = + "Create and mark up a Thread Environment Block. Set FS or GS segments to point to it."; + + protected static final String ADDRESS_OPTION_NAME = "Starting Address of the TEB"; + protected static final String ADDRESS_OPTION_DESCRIPTION = + "Address in RAM where TEB is located (must not be mapped to another block)"; + protected static final String ADDRESS_OPTION_DEFAULT_VALUE = ""; + + protected static final String VERSION_OPTION_NAME = "Windows OS Version"; + protected static final String VERSION_OPTION_DESCRIPTION = + "Version of the TEB fields to lay down. Many common fields persist across multiple OS versions."; + protected static final WinVersion VERSION_OPTION_DEFAULT_VALUE = WinVersion.WIN_7; + + protected String tebAddressString = ADDRESS_OPTION_DEFAULT_VALUE; + protected WinVersion winVersion = VERSION_OPTION_DEFAULT_VALUE; + + public TEBAnalyzer() { + super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER); + setPriority(AnalysisPriority.FORMAT_ANALYSIS.after().after()); + setDefaultEnablement(true); + } + + @Override + public boolean canAnalyze(Program program) { + if (!program.getLanguageID().getIdAsString().startsWith("x86")) { + return false; + } + return PEUtil.isVisualStudioOrClangPe(program); + } + + private Address findBlockLocation(Program program, boolean is64Bit, int blockSize) { + long offset = is64Bit ? 0xff00000000L : 0xffdff000L; + Memory memory = program.getMemory(); + AddressSpace addrSpace = program.getAddressFactory().getDefaultAddressSpace(); + Address startAddr = addrSpace.getAddress(offset); + Address endAddr = startAddr.add(blockSize - 1); + if (!memory.intersects(startAddr, endAddr)) { + return startAddr; + } + MemoryBlock[] blocks = memory.getBlocks(); + for (MemoryBlock block : blocks) { + Address addr = block.getEnd(); + if (addr.getAddressSpace() != addrSpace) { + continue; + } + if (startAddr.compareTo(addr) < 0) { + startAddr = addr; + } + } + startAddr = startAddr.add(1); + return startAddr; + } + + private void setTEBAddress(Program program, ThreadEnvironmentBlock teb) { + long offset; + try { + offset = Long.parseLong(tebAddressString, 16); + } + catch (NumberFormatException ex) { + offset = 0; // Option is empty or bad format + } + Address addr; + if (offset == 0) { + addr = findBlockLocation(program, teb.is64(), teb.getBlockSize()); + } + else { + addr = program.getAddressFactory().getDefaultAddressSpace().getAddress(offset); + } + teb.setAddress(addr); + } + + @Override + public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) + throws CancelledException { + MemoryBlock block = program.getMemory().getBlock(ThreadEnvironmentBlock.BLOCK_NAME); + if (block != null) { + return true; + } + ThreadEnvironmentBlock teb = new ThreadEnvironmentBlock(program, winVersion); + setTEBAddress(program, teb); + boolean commit = true; + int transactionID = program.startTransaction("Thread Environment Block"); + try { + teb.createBlocksAndSymbols(); + teb.setRegisterValue(); + } + catch (Exception e) { + Msg.error(this, "Unable to create the Thread Environment Block"); + commit = false; + } + program.endTransaction(transactionID, commit); + return true; + } + + @Override + public void registerOptions(Options options, Program program) { + options.registerOption(ADDRESS_OPTION_NAME, tebAddressString, null, + ADDRESS_OPTION_DESCRIPTION); + options.registerOption(VERSION_OPTION_NAME, winVersion, null, VERSION_OPTION_DESCRIPTION); + } + + @Override + public void optionsChanged(Options options, Program program) { + tebAddressString = options.getString(ADDRESS_OPTION_NAME, tebAddressString); + winVersion = options.getEnum(VERSION_OPTION_NAME, winVersion); + } +} diff --git a/Ghidra/Processors/x86/data/languages/x86-64-win.cspec b/Ghidra/Processors/x86/data/languages/x86-64-win.cspec index ee5e0edf55..7d440564a2 100644 --- a/Ghidra/Processors/x86/data/languages/x86-64-win.cspec +++ b/Ghidra/Processors/x86/data/languages/x86-64-win.cspec @@ -87,6 +87,7 @@ + @@ -148,6 +149,7 @@ + diff --git a/Ghidra/Processors/x86/data/languages/x86win.cspec b/Ghidra/Processors/x86/data/languages/x86win.cspec index 29c7bb9228..c07b7e4d33 100644 --- a/Ghidra/Processors/x86/data/languages/x86win.cspec +++ b/Ghidra/Processors/x86/data/languages/x86win.cspec @@ -59,6 +59,7 @@ + @@ -96,6 +97,7 @@ + @@ -138,6 +140,7 @@ + @@ -177,6 +180,7 @@ +