public static AnalyzedFile Analyze(string FilePath, string AsmPath = null) { PeFile File = new PeFile(FilePath); SortedList <UInt32, ArmInstruction> AnalyzedCode = new SortedList <uint, ArmInstruction>(0x1000000); // Default capacity of 0x100000 was not enough for analyzing ntoskrnl.exe if ((AsmPath != null) && System.IO.File.Exists(AsmPath)) { using (StreamReader Reader = new StreamReader(AsmPath)) { while (Reader.Peek() >= 0) { ArmInstruction Instruction = new ArmInstruction(Reader.ReadLine()); AnalyzedCode.Add(Instruction.Address, Instruction); } } } else { CapstoneDisassembler <Gee.External.Capstone.Arm.ArmInstruction, ArmRegister, ArmInstructionGroup, ArmInstructionDetail> Disassembler = CapstoneDisassembler.CreateArmDisassembler(DisassembleMode.ArmThumb); // Initially use a Dictionary and sort it afterwards. For analyzing ntoskrnl.exe this is about 60 times faster than using a SortedList from the start. // Default capacity of 0x100000 was not enough for analyzing ntoskrnl.exe Dictionary <UInt32, ArmInstruction> TempCode = new Dictionary <uint, ArmInstruction>(0x1000000); // Analyze from entrypoint Analyze(Disassembler, File.Sections, TempCode, (UInt32)(File.ImageBase + File.EntryPoint)); // Analyze from exports foreach (FunctionDescriptor Function in File.Exports) { Analyze(Disassembler, File.Sections, TempCode, (UInt32)(Function.VirtualAddress)); } // Analyze from imports foreach (FunctionDescriptor Function in File.Imports) { Analyze(Disassembler, File.Sections, TempCode, (UInt32)(Function.VirtualAddress)); } // Analyze from runtime-functions foreach (FunctionDescriptor Function in File.RuntimeFunctions) { Analyze(Disassembler, File.Sections, TempCode, (UInt32)(Function.VirtualAddress)); } // Sort the instructions. // SortedList is used, because it can be indexed by value (not only by key). List <UInt32> Keys = TempCode.Keys.ToList(); Keys.Sort(); foreach (UInt32 Key in Keys) { AnalyzedCode.Add(Key, TempCode[Key]); } if (AsmPath != null) { System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(AsmPath)); using (StreamWriter Writer = new StreamWriter(AsmPath, false)) { for (int i = 0; i < AnalyzedCode.Count; i++) { Writer.WriteLine(AnalyzedCode.Values[i].ToString()); } } } } return(new AnalyzedFile() { File = File, Code = AnalyzedCode }); }
public static void Analyze(CapstoneDisassembler <Gee.External.Capstone.Arm.ArmInstruction, ArmRegister, ArmInstructionGroup, ArmInstructionDetail> Disassembler, List <Section> Sections, Dictionary <UInt32, ArmInstruction> AnalyzedCode, UInt32 VirtualAddress) { VirtualAddress = VirtualAddress - (VirtualAddress % 2); List <UInt32> AddressesToAnalyze = new List <uint>(); AddressesToAnalyze.Add(VirtualAddress); Section CurrentSection = null; while (AddressesToAnalyze.Count > 0) { UInt32 CurrentAddress = AddressesToAnalyze[0]; if ((CurrentSection == null) || (CurrentAddress < CurrentSection.VirtualAddress) || (CurrentAddress > (CurrentSection.VirtualAddress + CurrentSection.VirtualSize))) { CurrentSection = Sections.Where(s => ((CurrentAddress >= s.VirtualAddress) && (CurrentAddress < (s.VirtualAddress + s.VirtualSize)) && s.IsCode)).FirstOrDefault(); if (CurrentSection == null) { // throw new Exception("Address 0x" + CurrentAddress.ToString("X8") + " is not inside boundaries of code-sections"); // Probably jumped to this address because data was disassembled as if it were code. Ignore this. // return; AddressesToAnalyze.RemoveAt(0); continue; } } if (AnalyzedCode.ContainsKey(CurrentAddress)) { // return; AddressesToAnalyze.RemoveAt(0); continue; } IEnumerable <Instruction <Gee.External.Capstone.Arm.ArmInstruction, ArmRegister, ArmInstructionGroup, ArmInstructionDetail> > NewInstructions = Disassembler.DisassembleStream(CurrentSection.Buffer, (int)CurrentAddress - (int)CurrentSection.VirtualAddress, CurrentAddress); if (NewInstructions.Count() > 0) { UInt32 StartAddress = (UInt32)NewInstructions.First().Address; UInt32 EndAddress = (UInt32)NewInstructions.Last().Address; ArmInstruction PreviousInstruction = null; foreach (Instruction <Gee.External.Capstone.Arm.ArmInstruction, ArmRegister, ArmInstructionGroup, ArmInstructionDetail> DisassemblerInstruction in NewInstructions) { // ArmInstruction Instruction = new ArmInstruction(DisassemblerInstruction); ArmInstruction Instruction = new ArmInstruction() { Address = (UInt32)DisassemblerInstruction.Address, Bytes = DisassemblerInstruction.Bytes, Mnemonic = DisassemblerInstruction.Mnemonic, Operand = DisassemblerInstruction.Operand.Replace("sb", "r9").Replace("sl", "r10").Replace("fp", "r11").Replace("ip", "r12") }; if (AnalyzedCode.ContainsKey((UInt32)Instruction.Address)) { break; } // Merge movw + movt into one command // movw r3, #0x6010 + movt r3, #0x1000 = mov r3, #0x10006010 UInt32 HighPart, LowPart; string HighString, LowString; if ((PreviousInstruction != null) && (PreviousInstruction.Mnemonic == "movt") && (Instruction.Mnemonic == "movw") && (PreviousInstruction.Operand.Split(new char[] { ',' })[0] == Instruction.Operand.Split(new char[] { ',' })[0])) { byte[] Combined = new byte[8]; System.Buffer.BlockCopy(PreviousInstruction.Bytes, 0, Combined, 0, 4); System.Buffer.BlockCopy(Instruction.Bytes, 0, Combined, 4, 4); PreviousInstruction.Bytes = Combined; PreviousInstruction.Mnemonic = "mov"; HighString = PreviousInstruction.Operand.Substring(PreviousInstruction.Operand.IndexOf('#') + 1); if ((HighString.Length >= 2) && (HighString.Substring(0, 2) == "0x")) { HighPart = UInt32.Parse(HighString.Substring(2), System.Globalization.NumberStyles.HexNumber); } else { HighPart = UInt32.Parse(HighString); } LowString = Instruction.Operand.Substring(Instruction.Operand.IndexOf('#') + 1); if ((LowString.Length >= 2) && (LowString.Substring(0, 2) == "0x")) { LowPart = UInt32.Parse(LowString.Substring(2), System.Globalization.NumberStyles.HexNumber); } else { LowPart = UInt32.Parse(LowString); } PreviousInstruction.Operand = PreviousInstruction.Operand.Substring(0, PreviousInstruction.Operand.IndexOf('#') + 1) + "0x" + ((HighPart << 16) + LowPart).ToString("X8"); continue; } if ((PreviousInstruction != null) && (PreviousInstruction.Mnemonic == "movw") && (Instruction.Mnemonic == "movt") && (PreviousInstruction.Operand.Split(new char[] { ',' })[0] == Instruction.Operand.Split(new char[] { ',' })[0])) { byte[] Combined = new byte[8]; System.Buffer.BlockCopy(PreviousInstruction.Bytes, 0, Combined, 0, 4); System.Buffer.BlockCopy(Instruction.Bytes, 0, Combined, 4, 4); PreviousInstruction.Bytes = Combined; PreviousInstruction.Mnemonic = "mov"; HighString = Instruction.Operand.Substring(Instruction.Operand.IndexOf('#') + 1); if ((HighString.Length >= 2) && (HighString.Substring(0, 2) == "0x")) { HighPart = UInt32.Parse(HighString.Substring(2), System.Globalization.NumberStyles.HexNumber); } else { HighPart = UInt32.Parse(HighString); } LowString = PreviousInstruction.Operand.Substring(PreviousInstruction.Operand.IndexOf('#') + 1); if ((LowString.Length >= 2) && (LowString.Substring(0, 2) == "0x")) { LowPart = UInt32.Parse(LowString.Substring(2), System.Globalization.NumberStyles.HexNumber); } else { LowPart = UInt32.Parse(LowString); } PreviousInstruction.Operand = PreviousInstruction.Operand.Substring(0, PreviousInstruction.Operand.IndexOf('#') + 1) + "0x" + ((HighPart << 16) + LowPart).ToString("X8"); continue; } AnalyzedCode.Add((UInt32)Instruction.Address, Instruction); int IndexOfIndirectConstant = Instruction.Operand.IndexOf("[pc, #0x"); if (IndexOfIndirectConstant >= 0) { int IndexOfEnd = Instruction.Operand.IndexOf("]", IndexOfIndirectConstant); string PCOffsetString = Instruction.Operand.Substring(IndexOfIndirectConstant + 8, IndexOfEnd - IndexOfIndirectConstant - 8); UInt32 PCOffset = UInt32.Parse(PCOffsetString, System.Globalization.NumberStyles.HexNumber); UInt32 PC = (UInt32)Instruction.Address + 4; UInt32 PCforIndirect = PC - (PC % 4); UInt32 VirtualAddressOfIndirectConstant = PCforIndirect + PCOffset; // If the address is outside the range of the section, then this is probably data which is compiled as code. // In this case we will ignore this and not do this part of the analysis. if ((VirtualAddressOfIndirectConstant >= CurrentSection.VirtualAddress) && (VirtualAddressOfIndirectConstant < (CurrentSection.VirtualAddress + CurrentSection.VirtualSize))) { UInt32 RawOffsetOfIndirectConstant = VirtualAddressOfIndirectConstant - CurrentSection.VirtualAddress; UInt32 IndirectConstant = BitConverter.ToUInt32(CurrentSection.Buffer, (int)RawOffsetOfIndirectConstant); Instruction.Operand = Instruction.Operand.Substring(0, IndexOfIndirectConstant) + "#0x" + IndirectConstant.ToString("x8") + Instruction.Operand.Substring(IndexOfEnd + 1); } } if (JumpCommands.Contains(Instruction.Mnemonic)) { UInt32 NewAddress = UInt32.Parse(Instruction.Operand.Substring(Instruction.Operand.IndexOf("#0x") + 3), System.Globalization.NumberStyles.HexNumber); NewAddress = NewAddress - (NewAddress % 2); if (((NewAddress < StartAddress) || (NewAddress > EndAddress)) && !AddressesToAnalyze.Any(a => a == NewAddress)) { AddressesToAnalyze.Add(NewAddress); } } PreviousInstruction = Instruction; } } AddressesToAnalyze.RemoveAt(0); } }