Beispiel #1
0
 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;
 }
Beispiel #2
0
        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));
            }
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
            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);
                }
            }
Beispiel #5
0
        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();
        }
Beispiel #6
0
            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);
            }