Пример #1
0
        public void Set(byte[] data)
        {
            // The input data must be the size of $size * 2. Otherwise there would be no need for a SplitRegisterHandle.

            // Set $upper to the upper bytes of $data (Take $size bytes from the end of $data)
            Upper.Set(Bitwise.Subarray(data, (int)Size));

            // Set $lower to the lower $size bytes of $data.
            Lower.Set(Bitwise.Cut(data, (int)Size));
        }
Пример #2
0
        public void Set(byte[] data)
        {
            // There are flaws in the caller if this is not already the same size.
            data = Bitwise.Cut(data, (int)Capacity);

            // If the destination handle is to DI, it would be a pointer not the actual value of DI
            if (Destination.Code == XRegCode.DI)
            {
                ControlUnit.SetMemory(DestPtr, data);
            }

            // Otherwise set the value of the register
            else
            {
                Destination.Set(data);
            }
        }
Пример #3
0
        public override void Execute()
        {
            // Fetch the operand. There will only be one because it is always the A register which is destination and source.
            byte[] Bytes = Fetch()[0];

            // Half the fetched bytes(because the capacity is that of the destination, see summary), and sign extend it
            // back to its former size.
            // E.g, in pseudo,
            //  CBW()
            //  {
            //      Bytes = $AX;
            //      Bytes = Cut($Bytes, 1);
            //      AX = $Bytes;
            //  }
            // From here it would be very simple to create CWDE and CDQE methods.
            // Fortunately because of the interface the Opcode base class provides, this can be generalised.
            Set(Bitwise.SignExtend(Bitwise.Cut(Bytes, (int)Capacity / 2), (byte)Capacity));
        }
Пример #4
0
 public Lea(DecodedTypes.ModRM input, OpcodeSettings settings = OpcodeSettings.NONE) : base("LEA", input, settings)
 {
     // EffectiveAddress property in the ModRM can be very helpful in the opcode. It is always a ulong, therefore does
     // have to be cut to the correct size(nothing will happen in QWORD operation).
     SourceAddress = Bitwise.Cut(BitConverter.GetBytes(input.EffectiveAddress), (int)Capacity);
 }
Пример #5
0
        public static ELF Parse(FileStream reader)
        {
            ELF ParsedElf;

            // Check if the length is less than the minimum acceptable.
            if (reader.Length < 0x34)
            {
                Logger.Log(LogCode.IO_INVALIDFILE, "Incomplete ELF header");
                return(null);
            }

            // Determine the class of the elf. If the class byte is equal to 1, the ELF is 32 bit, 2 for 64-bit. Otherwise assume an invalid elf.
            // The class byte is the 4th byte(0,1,2,3,4<-) so make sure the stream is at position 4. This is required to know the length of the header.
            reader.Seek(4, SeekOrigin.Begin);
            byte[] Elf_class = new byte[1];
            reader.Read(Elf_class, 0, 1);

            // Read and parse the rest of the header.
            // All bytes are offset slightly because 5 bytes(magic bytes + class) have already been parsed.
            // The offsets differ between elf32 and elf64 because addresses in elf64 must be 8 bytes; 4 bytes in elf32.
            byte[] FileHeader;
            switch (Elf_class[0])
            {
            case 0x01:
                FileHeader = new byte[0x34 - 0x05];
                reader.Read(FileHeader, 0, FileHeader.Length);
                ParsedElf = new ELF()
                {
                    Class = 32,

                    // The address of the section header table.
                    SHOffset = BitConverter.ToUInt32(FileHeader, 0x20 - 0x05),

                    // The length of each header in the section header table.
                    SHLength = BitConverter.ToUInt16(FileHeader, 0x2E - 0x05),

                    // The number of section headers in the section header table.
                    SHCount = BitConverter.ToUInt16(FileHeader, 0x30 - 0x05),

                    // The index of the section header containing the string names of all other sections in the section header table.
                    SHStrIndex = BitConverter.ToUInt16(FileHeader, 0x32 - 0x05)
                };
                break;

            case 0x02:
                FileHeader = new byte[0x40 - 0x05];
                reader.Read(FileHeader, 0, FileHeader.Length);
                ParsedElf = new ELF()
                {
                    Class      = 64,
                    SHOffset   = BitConverter.ToUInt64(FileHeader, 0x28 - 0x05),
                    SHLength   = BitConverter.ToUInt16(FileHeader, 0x3A - 0x05),
                    SHCount    = BitConverter.ToUInt16(FileHeader, 0x3C - 0x05),
                    SHStrIndex = BitConverter.ToUInt16(FileHeader, 0x3E - 0x05)
                };
                break;

            default:
                Logger.Log(LogCode.IO_INVALIDFILE, "Invalid class byte in ELF header");
                return(null);
            }
            ;

            // Make sure ELF uses x86-x64 instruction set
            if (FileHeader[0xD] != 0x03 && FileHeader[0xD] != 0x3E)
            {
                Logger.Log(LogCode.IO_INVALIDFILE, "Input elf does not use the x86 instruction set");
                return(null);
            }

            // Create a byte array with enough space to store the entire sh table.
            byte[] SHTable = new byte[ParsedElf.SHLength * ParsedElf.SHCount];

            // Seek the file to the offset of the SH table.
            reader.Seek((long)ParsedElf.SHOffset, SeekOrigin.Begin);

            // Read the entire SH table into $SHTable.
            reader.Read(SHTable, 0, ParsedElf.SHLength * ParsedElf.SHCount);

            // Use cut and subarray to cut out the shstrtab(section header string table; section that points to text names) from the SH table.
            // Subarray cuts out the preceeding tables. The length of which is the number of tables before shstrtab(which happens to be the index of shstrtab) multiplied by
            // the length of each table.
            byte[] SHstrtab = Bitwise.Cut(Bitwise.Subarray(SHTable, ParsedElf.SHLength * ParsedElf.SHStrIndex), ParsedElf.SHLength);

            // Get the size of strtab stored at offset 0x20 (elf32:0x14) of the section header and
            // seek to the position in the elf file of the actual strtab(not the shstrtab section header)
            int StrtabSize;

            if (ParsedElf.Class == 32)
            {
                StrtabSize = BitConverter.ToInt32(SHstrtab, 0x14);
                reader.Seek(BitConverter.ToUInt32(SHstrtab, 0x10), SeekOrigin.Begin);
            }
            else
            {
                StrtabSize = BitConverter.ToInt32(SHstrtab, 0x20);
                reader.Seek(BitConverter.ToUInt32(SHstrtab, 0x18), SeekOrigin.Begin);
            }

            byte[] strtab = new byte[StrtabSize];

            reader.Read(strtab, 0, StrtabSize);
            // Read sh_name from every section in shtab and check if the offset($shstrtab + $offset = target) is the string ".text".
            // .text is holds the user code and therefore is the only section this program cares about.
            // If there isn't one, which most likely means this wasn't an elf file, so handle it because this file cannot be used.
            for (int i = 0; i < ParsedElf.SHCount; i++)
            {
                if (ReadString(strtab, BitConverter.ToUInt32(SHTable, i * ParsedElf.SHLength)) == ".text")
                {
                    if (ParsedElf.Class == 32)
                    {
                        // Store the EntryPoint(in the file not when in memory) and TextLength(length of the
                        // code in bytes) in the elf object.
                        ParsedElf.EntryPoint = BitConverter.ToUInt32(SHTable, i * ParsedElf.SHLength + 0x10);
                        ParsedElf.TextLength = BitConverter.ToUInt32(SHTable, i * ParsedElf.SHLength + 0x14);
                    }
                    else
                    {
                        ParsedElf.EntryPoint = BitConverter.ToUInt64(SHTable, i * ParsedElf.SHLength + 0x18);
                        ParsedElf.TextLength = BitConverter.ToUInt64(SHTable, i * ParsedElf.SHLength + 0x20);
                    }
                    break;
                }
                else if (i + 1 == ParsedElf.SHCount)
                {
                    Logger.Log(LogCode.IO_INVALIDFILE, "ELF File had no .text section");
                    return(null);
                }
            }

            // Finally copy all the bytes from the .text segment into $Instructions.
            reader.Seek((long)ParsedElf.EntryPoint, SeekOrigin.Begin);
            ParsedElf.Instructions = new byte[(int)ParsedElf.TextLength];
            reader.Read(ParsedElf.Instructions, 0, (int)ParsedElf.TextLength);

            return(ParsedElf);
        }
Пример #6
0
            public async Task <TestcaseResult> RunTestcase()
            {
                TestcaseResult Result = new TestcaseResult();

                // Run every checkpoint. As they are ordered(this is automatically done due to the nature of how they are parsed), you will get half baked results if
                // one checkpoint is out of the code address range or in the middle of an instruction. This is not something that can be fixed from this end because my
                // code nor myself know what to expect as the results of your code if there is an error in the way that I execute your code. That is why I highly recommend
                // properly testing on a real machine and basing your testcase of that.
                for (int CheckpointNum = 0; CheckpointNum < CurrentTestcase.Checkpoints.Count; CheckpointNum++)
                {
                    // Run until the checkpoint
                    Run();

                    // This Snapshot variable will keep the code nice and readable.
                    Context Snapshot = Handle.ShallowCopy();

                    // If the instruction pointer is not an address in the checkpoint list, it must have ended prematurely, i.e not all checkpoints were handled before execution finished.
                    // This is more than likely an error in how the testcase was written.
                    Checkpoint CurrentCheckpoint;
                    if (!CurrentTestcase.Checkpoints.TryGetValue(Snapshot.InstructionPointer, out CurrentCheckpoint))
                    {
                        Logger.Log(LogCode.TESTCASE_RUNTIME, "");
                        break;
                    }

                    // Iterate over every type of test present and handle them slightly differently.
                    List <CheckpointSubresult> CurrentSubresults = new List <CheckpointSubresult>();
                    foreach (TestRegister testReg in CurrentCheckpoint.ExpectedRegisters)
                    {
                        // Disassemble the mnemonic, provided by RegisterHandle. This will be different to how it was input as the full mnemonic that is representative of the size is given rather than a shorthand.
                        string Mnemonic = testReg.Register.DisassembleOnce();

                        // This is the "Found" value.
                        byte[] ActualValue = testReg.Register.FetchOnce();

                        // If CompareTo() returns 0, they are equal. ExpectedValue is cut to the same size as the register just to be sure.
                        bool RegistersEqual = ActualValue.CompareTo(Bitwise.Cut(BitConverter.GetBytes(testReg.ExpectedValue), (int)testReg.Register.Size), false) == 0;

                        // AND $Passed by $RegistersEqual. This means that if $RegistersEqual is ever false, Passed will stay false for the entire testcase.
                        Result.Passed &= RegistersEqual;

                        CurrentSubresults.Add(
                            new CheckpointSubresult
                        {
                            Passed   = RegistersEqual,
                            Expected = $"${Mnemonic}=0x{testReg.ExpectedValue.ToString("X")}",
                            Found    = $"${Mnemonic}=0x{BitConverter.ToUInt64(Bitwise.ZeroExtend(testReg.Register.FetchOnce(), 8), 0).ToString("X")}"
                        });
                    }
                    foreach (TestMemoryAddress testMem in CurrentCheckpoint.ExpectedMemory)
                    {
                        // ExactAddress will hold the starting address of the memory to compare.
                        ulong ExactAddress = 0;

                        // Build up the mnemonic throughout based on the conditions.
                        string Mnemonic = "";

                        // If there is an offset register(It would be null if there was not as TestMemoryAddress is a struct),
                        if (testMem.OffsetRegister != null)
                        {
                            // Use the RegisterHandle to do the disassembly.
                            Mnemonic = testMem.OffsetRegister.DisassembleOnce();

                            // This nested condition is unavoidable as there are certain things that need to happen if there is an offset register as well
                            // as an offset, and things that need to happen both ways if there is an offset at all.
                            if (testMem.Offset != 0)
                            {
                                // Append a +/- based on the offset as the absolute value is taken later. Just for quality of life really, It's alot easier than
                                // having to work out the magnitude of a twos compliment value in your head, even if its just subtraction it still leaves unnecessary
                                // work for the user.
                                Mnemonic += (testMem.Offset > 0) ? "+" : "-";
                            }

                            // Add the value of the register onto $ExactAddress.
                            ExactAddress += BitConverter.ToUInt64(testMem.OffsetRegister.FetchOnce(), 0);
                        }

                        // This secretly does two things. If there was no offset at all(no attribute), it was never assigned to into the struct, therefore its default
                        // value would be zero. Also, if offset was zero with an offset_register, its not very useful to see, so only the offset_register will be shown.
                        if (testMem.Offset != 0)
                        {
                            // The absolute value is taken because a negative is not possible at this point unless there was an offset register. In that case, a +/- was already
                            // added to show the sign.
                            Mnemonic += $"0x{Math.Abs(testMem.Offset).ToString("X")}";

                            // Finally add the offset onto $ExactAddress
                            ExactAddress += (ulong)testMem.Offset;
                        }

                        // Assume equality at first.
                        bool MemoryEqual = true;

                        // Create a new aob for the "Found" element.
                        byte[] FoundBytes = new byte[testMem.ExpectedBytes.Length];

                        // Compare the bytes in $Snapshot.Memory with the bytes in $ExpectedBytes
                        for (uint ByteOffset = 0; ByteOffset < testMem.ExpectedBytes.Length; ByteOffset++)
                        {
                            FoundBytes[ByteOffset] = Snapshot.Memory[ExactAddress + ByteOffset];
                            if (Snapshot.Memory[ExactAddress + ByteOffset] != testMem.ExpectedBytes[ByteOffset])
                            {
                                // If there is one or more bytes different, it must be false. A break cannot be added here
                                // because FoundBytes is still being filled up.
                                MemoryEqual = false;
                            }
                        }

                        // Same as with register.
                        Result.Passed &= MemoryEqual;

                        CurrentSubresults.Add(
                            new CheckpointSubresult
                        {
                            Passed   = MemoryEqual,
                            Expected = $"[{Mnemonic}]={{{ParseBytes(testMem.ExpectedBytes)}}}",
                            Found    = $"[{Mnemonic}]={{{ParseBytes(FoundBytes)}}}"
                        });
                    }
                    if (CurrentCheckpoint.TestFlags)
                    {
                        // A lot of the heavy work here is done by FlagSet.

                        // This method will ignore all flags that are FlagState.UNDEFINED in $ExpectedFlags but still compare the flags that do have a value.
                        // This makes sure flags do not have to be exhaustively specified in the testcase file. If only the carry flag is specified in an element,
                        // only the carry flag will be considered.
                        bool FlagsEqual = CurrentCheckpoint.ExpectedFlags.EqualsOrUndefined(ControlUnit.Flags);
                        Result.Passed &= FlagsEqual;
                        CurrentSubresults.Add(
                            new CheckpointSubresult
                        {
                            Passed   = FlagsEqual,
                            Expected = CurrentCheckpoint.ExpectedFlags.ToString(),

                            // This produces a string representation of all the flags that are not FlagState.UNDEFINED in $Snapshot.Flags and $ExpectedFlags.
                            Found = Snapshot.Flags.And(CurrentCheckpoint.ExpectedFlags).ToString()
                        });
                    }
                    Result.CheckpointOutputs.Add(CurrentCheckpoint, CurrentSubresults);
                }
                return(Result);
            }