Decode Windows resources from PE.

This commit is contained in:
2018-03-09 15:29:57 +00:00
parent b11468bfdc
commit e18d4c26c3
3 changed files with 169 additions and 17 deletions

View File

@@ -195,8 +195,45 @@ namespace libexeinfo
sb.AppendFormat("\tExecutable contains debug information of type {0}", debugDirectory.type)
.AppendLine();
if(WindowsResourcesRoot != null)
{
sb.AppendLine("\tResources:");
PrintResourceNode(sb, WindowsResourcesRoot, 2);
}
return sb.ToString();
}
}
static void PrintResourceNode(StringBuilder sb, ResourceNode node, int level)
{
for(int i = 0; i < level; i++) sb.Append("\t");
if(node.children != null)
{
switch(node.level)
{
case 0:
sb.AppendFormat("Root contains {0} types:", node.children.Length).AppendLine();
break;
case 1:
sb.AppendFormat("Type {0} has {1} items:", node.name, node.children.Length).AppendLine();
break;
case 2:
sb.AppendFormat("ID {0} has {1} languages:", node.id, node.children.Length).AppendLine();
break;
default:
sb.AppendFormat("ID {0} has {1} items:", node.id, node.children.Length).AppendLine();
break;
}
foreach(ResourceNode child in node.children) PrintResourceNode(sb, child, level + 1);
}
if(node.data == null) return;
if(node.level == 3)
sb.AppendFormat("{0} contains {1} bytes.", node.name, node.data.Length).AppendLine();
else sb.AppendFormat("ID {0} contains {1} bytes.", node.id, node.data.Length).AppendLine();
}
}
}

View File

@@ -26,18 +26,18 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using libexeinfo.Windows;
namespace libexeinfo
{
/// <summary>
/// Represents a Microsoft Portable Executable
/// </summary>
// TODO: Process BeOS resources
// TODO: Process Windows resources
public partial class PE : IExecutable
{
MZ baseExecutable;
@@ -51,6 +51,7 @@ namespace libexeinfo
string[] importedNames;
string moduleName;
COFF.SectionHeader[] sectionHeaders;
public ResourceNode WindowsResourcesRoot;
WindowsHeader64 winHeader;
/// <summary>
@@ -367,23 +368,28 @@ namespace libexeinfo
debugDirectory = BigEndianMarshal.ByteArrayToStructureLittleEndian<DebugDirectory>(buffer);
}
// BeOS .rsrc is not virtual addressing, and has no size, solve it
if(reqOs.Name == "BeOS" && newSectionHeaders.ContainsKey(".rsrc"))
{
newSectionHeaders.TryGetValue(".rsrc", out COFF.SectionHeader beRsrc);
newSectionHeaders.Remove(".rsrc");
beRsrc.pointerToRawData = beRsrc.virtualAddress;
if(newSectionHeaders.TryGetValue(".rsrc", out COFF.SectionHeader rsrc))
if(reqOs.Name == "BeOS")
{
newSectionHeaders.Remove(".rsrc");
rsrc.pointerToRawData = rsrc.virtualAddress;
long maxPosition = BaseStream.Length;
foreach(KeyValuePair<string, COFF.SectionHeader> kvp in newSectionHeaders)
if(kvp.Value.pointerToRawData <= maxPosition &&
kvp.Value.pointerToRawData > beRsrc.pointerToRawData)
maxPosition = kvp.Value.pointerToRawData;
long maxPosition = BaseStream.Length;
foreach(KeyValuePair<string, COFF.SectionHeader> kvp in newSectionHeaders)
if(kvp.Value.pointerToRawData <= maxPosition &&
kvp.Value.pointerToRawData > rsrc.pointerToRawData)
maxPosition = kvp.Value.pointerToRawData;
beRsrc.sizeOfRawData = (uint)(maxPosition - beRsrc.pointerToRawData);
beRsrc.virtualSize = beRsrc.sizeOfRawData;
newSectionHeaders.Add(".rsrc", beRsrc);
}
rsrc.sizeOfRawData = (uint)(maxPosition - rsrc.pointerToRawData);
rsrc.virtualSize = rsrc.sizeOfRawData;
newSectionHeaders.Add(".rsrc", rsrc);
// TODO: Decode BeOS resource format
}
else
WindowsResourcesRoot = GetResourceNode(BaseStream, rsrc.pointerToRawData,
rsrc.virtualAddress,
rsrc.pointerToRawData, 0, null, 0);
sectionHeaders = newSectionHeaders.Values.OrderBy(s => s.pointerToRawData).ToArray();
Segment[] segments = new Segment[sectionHeaders.Length];
@@ -401,6 +407,105 @@ namespace libexeinfo
Strings = strings;
}
static ResourceNode GetResourceNode(Stream stream, long position, long rsrcVa, long rsrcStart, uint id,
string name, int level)
{
long oldPosition = stream.Position;
ResourceNode thisNode = new ResourceNode {name = name, id = id, level = level};
if(thisNode.name == null)
thisNode.name = level == 1 ? Resources.IdToName((ushort)thisNode.id) : $"{thisNode.id}";
stream.Position = position;
byte[] buffer = new byte[Marshal.SizeOf(typeof(ResourceDirectoryTable))];
stream.Read(buffer, 0, buffer.Length);
ResourceDirectoryTable rsrcTable =
BigEndianMarshal.ByteArrayToStructureLittleEndian<ResourceDirectoryTable>(buffer);
buffer = new byte[Marshal.SizeOf(typeof(ResourceDirectoryEntries))];
ResourceDirectoryEntries[] entries =
new ResourceDirectoryEntries[rsrcTable.nameEntries + rsrcTable.idEntries];
for(int i = 0; i < rsrcTable.nameEntries; i++)
{
stream.Read(buffer, 0, buffer.Length);
entries[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian<ResourceDirectoryEntries>(buffer);
}
for(int i = 0; i < rsrcTable.idEntries; i++)
{
stream.Read(buffer, 0, buffer.Length);
entries[rsrcTable.nameEntries + i] =
BigEndianMarshal.ByteArrayToStructureLittleEndian<ResourceDirectoryEntries>(buffer);
}
thisNode.children = new ResourceNode[entries.Length];
for(int i = 0; i < rsrcTable.nameEntries; i++)
{
byte[] len = new byte[2];
stream.Position = rsrcStart + (entries[i].nameOrID & 0x7FFFFFFF);
stream.Read(len, 0, 2);
buffer = new byte[BitConverter.ToUInt16(len, 0) * 2];
stream.Read(buffer, 0, buffer.Length);
string childName = Encoding.Unicode.GetString(buffer);
if((entries[i].rva & 0x80000000) == 0x80000000)
thisNode.children[i] = GetResourceNode(stream, rsrcStart + (entries[i].rva & 0x7FFFFFFF), rsrcVa,
rsrcStart, 0,
childName, level + 1);
else
{
buffer = new byte[Marshal.SizeOf(typeof(ResourceDataEntry))];
stream.Position = rsrcStart + (entries[i].rva & 0x7FFFFFFF);
stream.Read(buffer, 0, buffer.Length);
ResourceDataEntry dataEntry =
BigEndianMarshal.ByteArrayToStructureLittleEndian<ResourceDataEntry>(buffer);
thisNode.children[i] = new ResourceNode
{
data = new byte[dataEntry.size],
id = 0,
name = childName,
level = level + 1
};
stream.Position = dataEntry.rva - (rsrcVa - rsrcStart);
stream.Read(thisNode.children[i].data, 0, (int)dataEntry.size);
}
}
for(int i = rsrcTable.nameEntries; i < rsrcTable.nameEntries + rsrcTable.idEntries; i++)
if((entries[i].rva & 0x80000000) == 0x80000000)
thisNode.children[i] = GetResourceNode(stream, rsrcStart + (entries[i].rva & 0x7FFFFFFF), rsrcVa,
rsrcStart, entries[i].nameOrID & 0x7FFFFFFF, null,
level + 1);
else
{
buffer = new byte[Marshal.SizeOf(typeof(ResourceDataEntry))];
stream.Position = rsrcStart + (entries[i].rva & 0x7FFFFFFF);
stream.Read(buffer, 0, buffer.Length);
ResourceDataEntry dataEntry =
BigEndianMarshal.ByteArrayToStructureLittleEndian<ResourceDataEntry>(buffer);
thisNode.children[i] = new ResourceNode
{
data = new byte[dataEntry.size],
id = entries[i].nameOrID & 0x7FFFFFFF,
name = $"{entries[i].nameOrID & 0x7FFFFFFF}",
level = level + 1
};
if(level == 2)
try { thisNode.children[i].name = new CultureInfo((int)thisNode.children[i].id).DisplayName; }
catch { thisNode.children[i].name = $"Language ID {thisNode.children[i].id}"; }
stream.Position = dataEntry.rva - (rsrcVa - rsrcStart);
stream.Read(thisNode.children[i].data, 0, (int)dataEntry.size);
}
stream.Position = oldPosition;
return thisNode;
}
/// <summary>
/// Identifies if the specified executable is a Microsoft Portable Executable
/// </summary>

View File

@@ -24,6 +24,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
using static libexeinfo.COFF;
@@ -449,5 +450,14 @@ namespace libexeinfo
/// </summary>
public uint importAddressTableRva;
}
public class ResourceNode
{
public uint id;
public string name;
public ResourceNode[] children;
public byte[] data;
public int level;
}
}
}