// Detect and defeat various kinds of XOR encryption public void PostProcessImage <T>(FileFormatStream <T> stream, PluginPostProcessImageEventInfo data) where T : FileFormatStream <T> { if (stream is ElfReader32 stream32) { elf32 = stream32; } else if (stream is ElfReader64 stream64) { elf64 = stream64; } else { return; } PluginServices.For(this).StatusUpdate("Detecting encryption"); this.stream = stream; sections = stream.GetSections().GroupBy(s => s.Name).ToDictionary(s => s.Key, s => s.First()); if (HasDynamicEntry(Elf.DT_INIT) && sections.ContainsKey(".rodata")) { // Use the data section to determine some possible keys // If the data section uses striped encryption, bucketing the whole section will not give the correct key var roDataBytes = stream.ReadBytes(sections[".rodata"].ImageStart, sections[".rodata"].ImageLength); var xorKeyCandidateStriped = roDataBytes.Take(1024).GroupBy(b => b).OrderByDescending(f => f.Count()).First().Key; var xorKeyCandidateFull = roDataBytes.GroupBy(b => b).OrderByDescending(f => f.Count()).First().Key; // Select test nibbles and values for ARM instructions depending on architecture (ARMv7 / AArch64) var testValues = new Dictionary <int, (int, int, int, int)> {
public static PluginPostProcessImageEventInfo PostProcessImage <T>(FileFormatStream <T> stream) where T : FileFormatStream <T> => PluginManager.Try <ILoadPipeline, PluginPostProcessImageEventInfo>((p, e) => p.PostProcessImage(stream, e));
// See: FileFormatStreams/FileFormatStream.cs // See: FileFormatStreams/*Reader.cs // See: FileFormatStreams/Export.cs // See: FileFormatStreams/Section.cs // See: FileFormatStreams/Symbol.cs public void PostProcessImage <T>(FileFormatStream <T> stream, PluginPostProcessImageEventInfo data) where T : FileFormatStream <T> { // This is called once FOR EACH binary image // Regular ELF, PE, MachO files will result in a single call // Binaries containing sub-images (eg. Fat MachO, multi-architecture/split APKs) will result in // one call for the complete image and one call for each sub-image // FileFormatStream<T> derives from BinaryObjectReader and implements IFileFormatStream // Example: ignore Fat MachO files (wait for a sub-image) if (stream is UBReader) { return; } // Example: select an image extracted from a Fat MachO file if (stream is MachOReader32) { } if (stream is MachOReader64) { } // Useful properties: // Length, Numimages, DefaultFilename, IsModified, Images[], Position, Format, Arch, Bits, // GlobalOffset, ImageBase etc. // Example: check if file is ARM64 var isArm64 = stream.Arch == "ARM64"; // Example: check if file is 64-bit PE file var isPE64 = stream.Format == "PE32+"; // Useful methods: // GetSymbolTable(), GetFunctionTable(), GetExports(), GetSections() // MapVATR() (maps a virtual address to a file offset), MapFileOffsetToVA() (the opposite) etc. // Example: get all sections in file /* Section 00004370 0000000000008370 __text * Section 01f48058 0000000001f4c058 __picsymbolstub4 * Section 01f4aba8 0000000001f4eba8 __stub_helper * Section 01f4cbe8 0000000001f50be8 __gcc_except_tab ... */ if (stream.TryGetSections(out var sections)) { foreach (var section in sections) { Console.WriteLine($"Section {section.ImageStart:x8} {section.VirtualStart:x16} {section.Name}"); } } // You can use ReadWord and ReadWordArray to read a uint or ulong depending on whether the file is 32 or 64-bit // You can use ReadMappedX versions of all the Read functions to read from a virtual address mapped to the file // Example: Read 16 bytes from wherever virtual address 0x12345678 maps to in the file: try { var bytes = stream.ReadMappedBytes(0x12345678, 16); } catch { } // ReadMappedObjectPointerArray reads a list of VA pointers from the specified mapped VA, // then reads all of the objects // Example: VA 0x4000000 maps to 0x2000 in the file and contains 3 pointers, 0x5000010, 0x5000020, 0x5000440 // which are the VAs of three 'ObjectInMemory' objects: try { var objs = stream.ReadMappedObjectPointerArray <ObjectInMemory>(0x4000000, 3); } catch { } // Set data.IsStreamModified if you change the stream contents }
// Generate analytics from binary image public void PostProcessImage <T>(FileFormatStream <T> stream, PluginPostProcessImageEventInfo info) where T : FileFormatStream <T> { // Report to the user what is happening PluginServices.For(this).StatusUpdate("Generating analytics"); // Get the section we would like to investigate Section section; try { section = stream.GetSections().Single(s => s.Name == sectionName.Value); } // Not all binaries have a section with this name, or any sections at all catch { return; } // Get contents of section var bytes = stream.ReadBytes(section.ImageStart, (int)(section.ImageEnd - section.ImageStart)); // Produce frequency graph of bytes // This will produce a dictionary where the key is the byte value and the value is the number of times it occurred var freq = bytes.GroupBy(b => b).OrderBy(g => g.Key).ToDictionary(g => g.Key, g => (double)g.Count() * 100 / bytes.Length); // Write as CSV file if (Path.GetExtension(outputPath.Value) == ".csv") { var csv = new StringBuilder(); csv.AppendLine("Byte,Count"); csv.Append(string.Join(Environment.NewLine, freq.Select(f => f.Key + "," + f.Value))); File.WriteAllText(outputPath.Value, csv.ToString()); return; } // Write as XLSX file (using nuget package Aspose.Cells) var wb = new Workbook(); var sheet = wb.Worksheets[0]; // Add headers sheet.Cells["A1"].PutValue("Byte"); sheet.Cells["B1"].PutValue("Count"); // Create number format style var style = new CellsFactory().CreateStyle(); style.Custom = "@"; // Text for hex bytes column // Add data for (var row = 0; row < freq.Count; row++) { sheet.Cells["A" + (row + 2)].PutValue($"{row:X2}"); sheet.Cells["A" + (row + 2)].SetStyle(style, true); sheet.Cells["B" + (row + 2)].PutValue(freq[(byte)row]); } // Create table var list = sheet.ListObjects[sheet.ListObjects.Add("A1", "B257", hasHeaders: true)]; list.TableStyleType = TableStyleType.TableStyleMedium6; // Create chart var chart = sheet.Charts[sheet.Charts.Add(ChartType.Column, 3, 3, 40, 26)]; chart.Title.Text = "Frequency graph of data bytes"; chart.Style = 3; chart.ValueAxis.IsLogarithmic = true; chart.ValueAxis.CrossAt = 0.001; chart.ValueAxis.IsAutomaticMaxValue = false; chart.ValueAxis.MaxValue = 100; chart.ValueAxis.LogBase = 2; // Set chart data chart.SetChartDataRange("A1:B257", isVertical: true); chart.NSeries.CategoryData = "A2:A257"; chart.NSeries[0].GapWidth = 0; // 'Count' data series is at index 0 now that category has been set chart.ShowLegend = false; // Save workbook wb.Save(outputPath.Value, SaveFormat.Xlsx); }