private static void WritePE(FileStream fs, CodeInformation code_info, bool verbose) { //header information Image_Header header = new Image_Header(); List <List <Image_IAT_Header> > IAT_Headers = new List <List <Image_IAT_Header> >(); List <Image_Import_Header> IIH_Headers = new List <Image_Import_Header>(); //correct alignment if necessary if (code_info.FileAlignment > 0) { header.OptionalHeader.FileAlignment = code_info.FileAlignment; } if (code_info.SectionAlignment > 0) { header.OptionalHeader.SectionAlignment = code_info.SectionAlignment; } //temporary variables byte[] temp; byte[] temp2; uint offset; uint offset_calculation; //Data Sections MemoryStream RData_Section = new MemoryStream(); MemoryStream Data_Section = new MemoryStream(); MemoryStream RData_stringTable = new MemoryStream(); //Virtual Address locations uint Code_RVA = header.OptionalHeader.SectionAlignment; uint RData_RVA = AlignTo(Code_RVA + (uint)code_info.Code.Length, header.OptionalHeader.SectionAlignment); uint Data_RVA = 0; //Import header offsets uint total_IAT_offset = 0; uint import_header_offset = 0; #region imports if (code_info.SymbolInfo.Count > 0) { /* * [IAT1]....[IAT_n] * [Import_Header1][Import_Header2]...[Import_Headers_n] * [IAT1]....[IAT_n] * for(0...n) [0000 + function_name_1 + 00]....[0000 + function_name_n + 00] */ //Add in the placeholder headers foreach (var DLL in code_info.SymbolInfo) { IIH_Headers.Add(new Image_Import_Header()); List <Image_IAT_Header> current_IAT = DLL.Functions.Select(function => new Image_IAT_Header()).ToList(); IAT_Headers.Add(current_IAT); } //handle the first IAT list for (int i = 0; i < IIH_Headers.Count; i++) { IIH_Headers[i].ImportAddressTableAddress = RData_RVA + total_IAT_offset; foreach (CodeInformation.FunctionInformation function in code_info.SymbolInfo[i].Functions) { foreach (var replacement in function.Replacements) { offset_calculation = header.OptionalHeader.ImageBase + RData_RVA + total_IAT_offset; code_info.Code[replacement + 0] = (byte)(offset_calculation >> 0); code_info.Code[replacement + 1] = (byte)(offset_calculation >> 8); code_info.Code[replacement + 2] = (byte)(offset_calculation >> 16); code_info.Code[replacement + 3] = (byte)(offset_calculation >> 24); } total_IAT_offset += 4; } total_IAT_offset += 4; } header.OptionalHeader.DataDirectory[12].VirtualAddress = RData_RVA; header.OptionalHeader.DataDirectory[12].Size = total_IAT_offset; import_header_offset += RData_RVA + total_IAT_offset + ((uint)IIH_Headers.Count + 1) * 0x14; header.OptionalHeader.DataDirectory[1].VirtualAddress = RData_RVA + total_IAT_offset; header.OptionalHeader.DataDirectory[1].Size = (((uint)IIH_Headers.Count) + 1) * 0x14; //handle the second IAT list for (int i = 0; i < IIH_Headers.Count; i++) { IIH_Headers[i].ImportLookUpTableAddress = import_header_offset; import_header_offset += ((uint)IAT_Headers[i].Count + 1) * 4; } //handle function names and what not for (int i = 0; i < IAT_Headers.Count; i++) { for (int j = 0; j < IAT_Headers[i].Count; j++) { var func = code_info.SymbolInfo[i].Functions[j]; IAT_Headers[i][j].Address = import_header_offset; //write ordinal + function name + null temp = BitConverter.GetBytes((short)func.Ordinal); RData_stringTable.Write(temp, 0, temp.Length); temp = Encoding.UTF8.GetBytes(func.FunctionName); RData_stringTable.Write(temp, 0, temp.Length); RData_stringTable.WriteByte(0); import_header_offset += 2 + (uint)temp.Length + 1; } IIH_Headers[i].NameAddress = import_header_offset; temp = Encoding.UTF8.GetBytes(code_info.SymbolInfo[i].LibraryName); RData_stringTable.Write(temp, 0, temp.Length); RData_stringTable.WriteByte(0); import_header_offset += (uint)temp.Length + 1; } foreach (var IAT in IAT_Headers) { IAT.Add(new Image_IAT_Header()); } //add in the null header to signify the end of the list IIH_Headers.Add(new Image_Import_Header()); //this is awful and it works //convert all headers into their byte equivalent, then concatenate them all together temp = IAT_Headers .Select(x => x .Select(y => y.ToBytes()) .Aggregate((a, b) => a .Concat(b) .ToArray())) .Aggregate((a, b) => a .Concat(b) .ToArray()); temp2 = IIH_Headers .Select(x => x.ToBytes()) .Aggregate((a, b) => a.Concat(b).ToArray()); RData_Section.Write(temp, 0, temp.Length); RData_Section.Write(temp2, 0, temp2.Length); RData_Section.Write(temp, 0, temp.Length); //CopyTo doesn't seem to work 100% of the time. Maybe a bug with .NET //RData_stringTable.CopyTo(RData_Section); temp = RData_stringTable.GetBuffer(); RData_Section.Write(temp, 0, (int)RData_stringTable.Length); } #endregion #region Data Table //Correct Data RVA Data_RVA = AlignTo((uint)(RData_RVA + RData_Section.Length), header.OptionalHeader.SectionAlignment); //string table replacements offset = 0; foreach (CodeInformation.StringTableInformation stringInfo in code_info.StringTable) { temp = Encoding.UTF8.GetBytes(stringInfo.Text); foreach (int replacement in stringInfo.Replacements) { offset_calculation = header.OptionalHeader.ImageBase + Data_RVA + offset; code_info.Code[replacement + 0] = (byte)(offset_calculation >> 0); code_info.Code[replacement + 1] = (byte)(offset_calculation >> 8); code_info.Code[replacement + 2] = (byte)(offset_calculation >> 16); code_info.Code[replacement + 3] = (byte)(offset_calculation >> 24); } Data_Section.Write(temp, 0, temp.Length); Data_Section.WriteByte(0); offset += (uint)(temp.LongLength + 1); } #endregion #region Header //I don't want to hard code this as 0x170 because I might (maybe) add in more sections, and forget to change this //though at the moment with what's coded already, we're wasting a huge amount of time calculating this temp = header.ToBytes(); uint raw_header_length = (uint)temp.LongLength; uint header_length = AlignTo(raw_header_length, header.OptionalHeader.FileAlignment); //.code section header.Text_Section.VirtualSize = (uint)code_info.Code.LongLength; header.Text_Section.SizeOfRawData = AlignTo((uint)code_info.Code.LongLength, header.OptionalHeader.FileAlignment); header.Text_Section.VirtualAddress = Code_RVA; header.Text_Section.PointerToRawData = header_length; //.rdata section header.RData_Section.VirtualSize = (uint)RData_Section.Length + 1; header.RData_Section.SizeOfRawData = AlignTo((uint)RData_Section.Length, header.OptionalHeader.FileAlignment); header.RData_Section.VirtualAddress = RData_RVA; header.RData_Section.PointerToRawData = header_length + header.Text_Section.SizeOfRawData; //.data section header.Data_Section.VirtualSize = (uint)Data_Section.Length; header.Data_Section.SizeOfRawData = AlignTo((uint)Data_Section.Length, header.OptionalHeader.FileAlignment); header.Data_Section.VirtualAddress = Data_RVA; header.Data_Section.PointerToRawData = header.RData_Section.PointerToRawData + header.RData_Section.SizeOfRawData; header.OptionalHeader.Subsystem = code_info.Subsystem; header.OptionalHeader.AddressOfEntryPoint = code_info.EntryPoint + Code_RVA; header.OptionalHeader.BaseOfCode = Code_RVA; header.OptionalHeader.BaseOfData = RData_RVA; header.OptionalHeader.SizeOfHeaders = AlignTo((uint)RData_Section.Length, header.OptionalHeader.FileAlignment) + AlignTo((uint)Data_Section.Length, header.OptionalHeader.FileAlignment); header.OptionalHeader.SizeOfInitializedData = header.OptionalHeader.SizeOfHeaders; header.OptionalHeader.SizeOfImage = AlignTo(header_length, header.OptionalHeader.SectionAlignment) + AlignTo(header.Text_Section.SizeOfRawData, header.OptionalHeader.SectionAlignment) + AlignTo(header.RData_Section.SizeOfRawData, header.OptionalHeader.SectionAlignment) + AlignTo(header.Data_Section.SizeOfRawData, header.OptionalHeader.SectionAlignment); header.OptionalHeader.SizeOfCode = AlignTo((uint)code_info.Code.LongLength, header.OptionalHeader.FileAlignment); header.FileHeader.TimeDateStamp = (uint)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; #endregion #region Write Executable //finally write the file //write PE header temp = header.ToBytes(); fs.Write(temp, 0, temp.Length); //write Header padding temp = CreatePadding((uint)fs.Position, header.OptionalHeader.FileAlignment); fs.Write(temp, 0, temp.Length); //write .code section fs.Write(code_info.Code, 0, code_info.Code.Length); //write .code padding temp = CreatePadding((uint)code_info.Code.Length, header.OptionalHeader.FileAlignment); fs.Write(temp, 0, temp.Length); //write .rdata section temp = RData_Section.GetBuffer(); fs.Write(temp, 0, (int)RData_Section.Length); //write .rdata padding temp = CreatePadding((uint)RData_Section.Length, header.OptionalHeader.FileAlignment); fs.Write(temp, 0, temp.Length); //write .data section temp = Data_Section.GetBuffer(); fs.Write(temp, 0, (int)Data_Section.Length); //write .data padding temp = CreatePadding((uint)Data_Section.Length, header.OptionalHeader.FileAlignment); fs.Write(temp, 0, temp.Length); #endregion }
public static void WriteEXE(FileStream fs, CodeInformation code_info, bool verbose) { WriteDOSHeader(fs); WritePE(fs, code_info, verbose); }