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)); }
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); } }
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)); }
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); }
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); }
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); }