public SplitRegisterHandle(XRegCode lower, XRegCode upper, SplitRegisterHandleSettings settings = SplitRegisterHandleSettings.NONE) { // Create the handles for the two registers ready so they do not have to be created more than once. Upper = new ControlUnit.RegisterHandle(upper, RegisterTable.GP); Lower = new ControlUnit.RegisterHandle(lower, RegisterTable.GP); Settings = settings; }
public void Execute() { // Handle a REP prefix. If the handle has NOJMP set, this is ignored. // See summary before reading. if (ControlUnit.LPrefixBuffer.Contains(PrefixByte.REPZ) || ControlUnit.LPrefixBuffer.Contains(PrefixByte.REPNZ) && (ControlUnit.CurrentHandle.HandleSettings | HandleParameters.NOJMP) != ControlUnit.CurrentHandle.HandleSettings) { // Create a handle to ECX ControlUnit.RegisterHandle CountHandle = new ControlUnit.RegisterHandle(XRegCode.C, RegisterTable.GP, RegisterCapacity.DWORD); // Initialise $Count to $ECX. uint Count = BitConverter.ToUInt32(CountHandle.FetchOnce(), 0); for (; Count > 0; Count--) { OnExecute(); OnInitialise(); // If the operation is a comparison, extra checks have to be done against the zero flag. // This will be ignored when not a comparison. // The conditions can be summarised as // If (REPZ && ZF != 1) break; // If (REPNZ && ZF != 0) break; if ((Settings | StringOpSettings.COMPARE) == Settings && ((ControlUnit.LPrefixBuffer.Contains(PrefixByte.REPZ) && ControlUnit.Flags.Zero != FlagState.ON) || // repz and repnz act as normal rep if the opcode isnt cmps or scas= (ControlUnit.LPrefixBuffer.Contains(PrefixByte.REPNZ) && ControlUnit.Flags.Zero != FlagState.OFF))) { break; } } // Set $ECX to the new count. Once the instruction is completely exected(all REPs handled), $ECX will either be // zero, or $ECX_Before_Instruction - $Number_Of_OnExecute()s CountHandle.Set(BitConverter.GetBytes(Count)); } // If no REP prefix, the instruction can be executed as normal. else { OnExecute(); } // If the destination or source were DI/SI, commit the value of their stored pointers to the value of the register. if (Destination.Code == XRegCode.DI) { Destination.Set(BitConverter.GetBytes(PtrSize == RegisterCapacity.QWORD ? DestPtr : (uint)DestPtr)); } if (Source.Code == XRegCode.SI) { Source.Set(BitConverter.GetBytes(PtrSize == RegisterCapacity.QWORD ? SrcPtr : (uint)SrcPtr)); } }
public override void Execute() { // Pop off a QWORD from the stack and jump to it. ControlUnit.Jump(BitConverter.ToUInt64(StackPop(RegisterCapacity.QWORD), 0)); // If there is an immediate operand, add it on to RSP. List <byte[]> Operands = Fetch(); if (Operands.Count > 0) { // Create a new handle to RSP ControlUnit.RegisterHandle StackPointer = new ControlUnit.RegisterHandle(XRegCode.SP, RegisterTable.GP, RegisterCapacity.QWORD); // Add the zero extended immediate. Important that it is not sign extended, or it could decrement the stack pointer. byte[] NewSP; Bitwise.Add(StackPointer.FetchOnce(), Bitwise.ZeroExtend(Operands[0], 8), out NewSP); // Set the new SP. StackPointer.Set(NewSP); } }
public bool TryParse(XElement testCriteria) { // This method attempts to parse a given XElement into a TestRegister. If there is an error it returns false. This allows for some simpler // error handling than if it was a constructor as the caller can decide the best course of action. // Here some checks are greatly compacted into a try catch block. // The scenarios where something could go wrong, // - Invalid register id attribute hence cannot be found in dictionary, KeyNotFoundException. // - Register has no id attribute, it is null, NullReferenceException. // - No size attribute, NullReferenceException. // - Size attribute is invalid, cannot be casted to int. // - No value in $testCriteria.Value, null etc. // - Value in $testCriteria.Value cannot be converted to a ulong. // All of those fit very nicely into a try catch block, which is much faster than validating all the input manually e.g with many ifs // but only when the input is valid. There is a large overhead when an exception is caught like this. However slowing down the proficient users // for the sake of others is not what I intend to do. try { // Retrieve the definition of the mnemoinic in the id attribute. (RegisterTable Table, XRegCode Code) = RegisterDecodeTable[testCriteria.Attribute("id").Value]; // Create a new register handle to it that will be used to compare the registers at runtime. Register = new ControlUnit.RegisterHandle(Code, Table, (RegisterCapacity)(int)testCriteria.Attribute("size")); // If the value that is to be compared is greater than the size of the register, the input must be invalid. It is doubled because it takes // two characters to represent a byte in hex. if (testCriteria.Value.Length > (int)Register.Size * 2) { return(false); } // Convert the value from hex to ulong. ExpectedValue = Convert.ToUInt64(testCriteria.Value, 16); return(true); } catch { return(false); } }
public StringOperation(string mnemonic, XRegCode destination, XRegCode source, StringOpSettings settings) { Settings = settings; // Determine capacity as well as an informal mnemonic convention. I haven't seen it defined anywhere, but // in many programs such as GDB and GCC, this is used/accepted. // BYTE => append "B" // WORD => append "W" // DWORD => append "D" // QWORD => append "Q" // E.g, // SCAS RAX, QWORD PTR [RSI] => SCASQ // CMPS BYTE PTR [RDI], BYTE PTR[RSI] => CMPSB // However in my program(as with numerous others), the operands will remain but // without BYTE PTR or equivalent. This does slightly contradict the purpose of // this convention, but I don't expect everyone using the program to know the // operands of every string operation off by heart(as each has a constant set of operands). if ((Settings | StringOpSettings.BYTEMODE) == Settings) { Capacity = RegisterCapacity.BYTE; mnemonic += 'B'; } else if ((ControlUnit.RexByte | REX.W) == ControlUnit.RexByte) { Capacity = RegisterCapacity.QWORD; mnemonic += 'Q'; } else if (ControlUnit.LPrefixBuffer.Contains(PrefixByte.SIZEOVR)) { Capacity = RegisterCapacity.WORD; mnemonic += 'W'; } else { Capacity = RegisterCapacity.DWORD; mnemonic += 'D'; } Mnemonic = mnemonic; // Determine and store the pointer size(There will always be at least one). E.g [RSI] or [ESI]. PtrSize = ControlUnit.LPrefixBuffer.Contains(PrefixByte.ADDROVR) ? RegisterCapacity.DWORD : RegisterCapacity.QWORD; // Create a handle to the destination. If the destination is DI, treat it as a pointer, as DI is always a pointer in a // string operation. destination == XRegCode.DI will also be checked later to mimic the same behaviour. Destination = new ControlUnit.RegisterHandle(destination, RegisterTable.GP, (destination == XRegCode.DI) ? PtrSize : Capacity); // Same as the above Source = new ControlUnit.RegisterHandle(source, RegisterTable.GP, (source == XRegCode.SI) ? PtrSize : Capacity); // Convert the two operands into pointers. These will only be used if their XRegCode is SI or DI, but stored regardless. // Saving these to a variable means that the registers themselves do not have to be operated on until the execution of // the opcode has finished. DestPtr = BitConverter.ToUInt64(Bitwise.ZeroExtend(Destination.FetchOnce(), 8), 0); SrcPtr = BitConverter.ToUInt64(Bitwise.ZeroExtend(Source.FetchOnce(), 8), 0); // Fetch and store the value operand. This will only be ever used if one operand is the A register. This is because // the value of the register is only needed to form the pointers above if the register is SI/DI. There can only // be one operand that is the A register, therefore only the value of that operand needs to be stored. If there // is no A register, SI will be stored in ValueOperand. ValueOperand = (Destination.Code == XRegCode.A) ? Destination.FetchAs(Capacity) : Source.FetchAs(Capacity); OnInitialise(); }
public bool TryParse(XElement input, out (LogCode, string) ErrorInfo) { // If none of the input data(bytes to be tested for at the address) can be parsed, throw the error. Its an edge case but better // than no measure at all. if (!Core.TryParseHex(input.Value, out ExpectedBytes)) { ErrorInfo = (LogCode.TESTCASE_PARSEFAIL, "Invalid value of expected memory."); return(false); } // For the following, when an attribute not present, a null is returned. That is the basis of these conditionals. // If there is an offset register, if (input.Attribute("offset_register") != null) { // Attempt to fetch the definition if the given mnemonic from the dictionary. (RegisterTable, XRegCode)Result; if (!RegisterDecodeTable.TryGetValue(input.Attribute("offset_register").Value, out Result)) { ErrorInfo = (LogCode.TESTCASE_PARSEFAIL, "Invalid value of offset register."); return(false); } // Store the register handle for comparison later. OffsetRegister = new ControlUnit.RegisterHandle(Result.Item2, Result.Item1, RegisterCapacity.QWORD); } // If there is an offset, if (input.Attribute("offset") != null) { // As explained in the summary, the value in offset is parsed as big endian. Core.ReverseEndian is very // robust, it does not check any validity of the input and only does its job. Other functionality such as // Core.TryParseHex() is left to do this. byte[] OffsetBytes; if (!Core.TryParseHex(Core.ReverseEndian(input.Attribute("offset").Value), out OffsetBytes)) { ErrorInfo = (LogCode.TESTCASE_PARSEFAIL, "Invalid hex bytes in memory offset attribute"); return(false); } // Also if the offset is negative and there is no offset register, the input cannot be valid. There is still the possibility to // escape this by putting a register that will be zero at that time, but at that point there is either a huge obvious problem that // will fail every testcase, or worse yet, your assembly code was wrong. Offset = BitConverter.ToInt64(Bitwise.SignExtend(OffsetBytes, 8), 0); if (Offset < 0 && OffsetRegister == null) { ErrorInfo = (LogCode.TESTCASE_BADCHECKPOINT, "Cannot have a negative memory offset without a register to offset it"); return(false); } } // To check memory there must be at least an offset or an offset register. else if (OffsetRegister == null) { ErrorInfo = (LogCode.TESTCASE_BADCHECKPOINT, "Must have an offset attribute, offset_register attribute, or both"); return(false); } // As this is a struct, the values have to be assigned to something in the constructor. ErrorInfo = (LogCode.NONE, null); return(true); }