private static string HashElf(ElfHeader elf, long basePosition, Stream input) { using (var sha1 = SHA1.Create()) { foreach (var program in elf.ProgramHeaders) { var type = (uint)program.Type; var flags = (uint)program.Flags; sha1.TransformBlock(BitConverter.GetBytes(type.Swap()), 0, 4, null, 0); sha1.TransformBlock(BitConverter.GetBytes(flags.Swap()), 0, 4, null, 0); if (program.Type == Elf.ProgramType.Loadable && program.MemorySize > 0) { sha1.TransformBlock(BitConverter.GetBytes(program.VirtualAddress.Swap()), 0, 8, null, 0); sha1.TransformBlock(BitConverter.GetBytes(program.MemorySize.Swap()), 0, 8, null, 0); if (program.FileSize > 0) { if (basePosition + program.FileOffset + program.FileSize > input.Length) { return(null); } input.Position = basePosition + program.FileOffset; var programBytes = input.ReadBytes((int)program.FileSize); sha1.TransformBlock(programBytes, 0, programBytes.Length, null, 0); } } } sha1.TransformFinalBlock(new byte[0], 0, 0); return(BitConverter.ToString(sha1.Hash).Replace("-", "").ToLowerInvariant()); } }
private static int Run(string inputPath, string patchPath, string outputPath, string verifyHash, bool addCave, long?desiredCaveAddress, long?desiredCaveSize, long?originalCodeSize) { using (var inputData = File.OpenRead(inputPath)) using (var patchData = File.OpenRead(patchPath)) { var inputElf = ElfHeader.Read(inputData); if (string.IsNullOrWhiteSpace(verifyHash) == false) { var inputHash = HashElf(inputElf, 0, inputData); if (inputHash != verifyHash) { Console.WriteLine("Hash of input ELF does not match:"); Console.WriteLine(" desired: {0}", verifyHash); Console.WriteLine(" actual: {0}", inputHash); return(1); } } int codeIndex = -1; long newCodeSize = 0; ulong caveVirtualAddress = 0; long caveFileOffset = 0; long caveFileSize = 0; if (addCave == true) { codeIndex = Array.FindIndex( inputElf.ProgramHeaders, ph => ph.Type == Elf.ProgramType.Loadable && (ph.Flags & Elf.ProgramFlags.Executable) != 0); if (codeIndex + 1 >= inputElf.ProgramHeaders.Length) { Console.WriteLine("No program following code program."); return(2); } var codeHeader = inputElf.ProgramHeaders[codeIndex + 0]; var nextHeader = inputElf.ProgramHeaders[codeIndex + 1]; if ((ulong)codeHeader.FileSize != codeHeader.MemorySize) { Console.WriteLine("Code file size does not match memory size!"); return(3); } if (originalCodeSize.HasValue == true && originalCodeSize.Value != codeHeader.FileSize) { Console.WriteLine("Original code size does not match:"); Console.WriteLine(" desired: {0}", originalCodeSize.Value); Console.WriteLine(" actual: {0}", codeHeader.FileSize); return(4); } caveFileSize = nextHeader.FileOffset - (codeHeader.FileOffset + codeHeader.FileSize); caveVirtualAddress = codeHeader.VirtualAddress + (ulong)codeHeader.FileSize; caveFileOffset = codeHeader.FileOffset + codeHeader.FileSize; if (desiredCaveAddress.HasValue == true && (ulong)desiredCaveAddress.Value != caveVirtualAddress) { Console.WriteLine("Desired cave virtual address does not match:"); Console.WriteLine(" desired: {0}", desiredCaveAddress.Value); Console.WriteLine(" actual: {0}", caveVirtualAddress); return(5); } if (desiredCaveSize.HasValue == true && desiredCaveSize.Value != caveFileSize) { Console.WriteLine("Desired cave size does not match:"); Console.WriteLine(" desired: {0}", desiredCaveSize.Value); Console.WriteLine(" actual: {0}", caveFileSize); return(6); } newCodeSize = nextHeader.FileOffset - codeHeader.FileOffset; codeHeader.FileSize = newCodeSize; codeHeader.MemorySize = (ulong)newCodeSize; inputElf.ProgramHeaders[codeIndex] = codeHeader; } var patchElf = ElfHeader.Read(patchData); var patchSectionIndex = Array.IndexOf(patchElf.SectionNames, ".patches"); if (patchSectionIndex < 0) { Console.WriteLine("No .patches section."); return(7); } var patchSection = patchElf.SectionHeaders[patchSectionIndex]; var patchCount = patchSection.FileSize / 32; var patchAddresses = new long[patchCount]; var patchSizes = new long[patchCount]; patchData.Position = patchSection.FileOffset; for (int i = 0; i < patchSection.FileSize / 32; i++) { var patchAddressSource = patchData.ReadValueS64(Endian.Big); var patchAddressStart = patchData.ReadValueS64(Endian.Big); var patchAddressEnd = patchData.ReadValueS64(Endian.Big); //var patchReserved = patchData.ReadValueS64(Endian.Big); patchData.Position += 8; if (patchAddressSource != patchAddressStart) { Console.WriteLine( "Patch #{0} has mismatching source and start ({1:X} vs {2:X})", i + 1, patchAddressSource, patchAddressStart); return(8); } patchAddresses[i] = patchAddressStart; patchSizes[i] = patchAddressEnd - patchAddressStart; } File.Copy(inputPath, outputPath, true); var attributes = File.GetAttributes(outputPath); if ((attributes & FileAttributes.ReadOnly) != 0) { attributes &= ~FileAttributes.ReadOnly; File.SetAttributes(outputPath, attributes); } using (var outputData = File.Open(outputPath, FileMode.Open, FileAccess.ReadWrite)) { if (addCave == true) { outputData.Position = outputData.Length; var sectionHeadersPosition = outputData.Position; var newSectionHeaders = new List <Elf.SectionHeader>(inputElf.SectionHeaders); newSectionHeaders.Add(new Elf.SectionHeader() { Name = 0x1C, Type = Elf.SectionType.Program, Flags = Elf.SectionFlags.Alloc | Elf.SectionFlags.Executable, VirtualAddress = caveVirtualAddress, FileOffset = caveFileOffset, FileSize = caveFileSize, Link = 0, Info = 0, Align = 4, EntrySize = 0, }); foreach (var newSectionHeader in newSectionHeaders) { newSectionHeader.Swap(); outputData.WriteStructure(newSectionHeader); } // Update section headers info outputData.Position = Marshal.OffsetOf(typeof(Elf.FileHeader), "SectionHeadersOffset").ToInt64(); outputData.WriteValueS64(sectionHeadersPosition, Endian.Big); outputData.Position = Marshal.OffsetOf(typeof(Elf.FileHeader), "SectionHeaderCount").ToInt64(); outputData.WriteValueU16((ushort)newSectionHeaders.Count, Endian.Big); // Update code program info var programHeaderPosition = inputElf.FileHeader.ProgramHeadersOffset + (codeIndex * Marshal.SizeOf(typeof(Elf.ProgramHeader))); outputData.Position = programHeaderPosition + Marshal.OffsetOf(typeof(Elf.ProgramHeader), "FileSize").ToInt64(); outputData.WriteValueS64(newCodeSize, Endian.Big); outputData.Position = programHeaderPosition + Marshal.OffsetOf(typeof(Elf.ProgramHeader), "MemorySize").ToInt64(); outputData.WriteValueS64(newCodeSize, Endian.Big); } for (int i = 0; i < patchCount; i++) { var inputOffset = patchElf.GetFileOffset((ulong)patchAddresses[i], (uint)patchSizes[i]); patchData.Position = inputOffset; var outputOffset = inputElf.GetFileOffset((ulong)patchAddresses[i], (uint)patchSizes[i]); outputData.Position = outputOffset; outputData.WriteFromStream(patchData, patchSizes[i]); } } } return(0); }