Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to update some text content in StringTable? #219

Closed
laomms opened this issue Nov 21, 2021 · 7 comments
Closed

How to update some text content in StringTable? #219

laomms opened this issue Nov 21, 2021 · 7 comments
Labels
pe Issues related to AsmResolver.PE question win32resources Issues related to AsmResolver.PE.Win32Resources

Comments

@laomms
Copy link

laomms commented Nov 21, 2021

For example, this file: C:\Windows\Branding\Basebrd\en-US\basebrd.dll.mui, I want change the 251 resourceId content to "Preview-Version":

pic

@Washi1337 Washi1337 added pe Issues related to AsmResolver.PE question win32resources Issues related to AsmResolver.PE.Win32Resources and removed enhancement labels Nov 21, 2021
@Washi1337
Copy link
Owner

Washi1337 commented Nov 21, 2021

Hi, unfortunately RT_STRING resources do not have rich support yet in AsmResolver. As indicated in #66, high-level representations of win32 resources is still very much a work in progress.

That being said, it is possible to edit a string manually. RT_STRING entries are almost directly reflected in the raw win32 resource directory structure, as can be seen in tools like CFF Explorer:

CFF Explorer

cff

AsmResolver follows this structure as well. So what you can do, is follow this path in the win32 resource directory down to the data entry of the string block you want to edit, manually swap the contents, then update the PE file with a new .rsrc section.

Example code
// Read raw PE file. 
var file = PEFile.FromFile("basebrd.dll.mui");

// Open as image.
var image = PEImage.FromFile(file);

// Look up string entry.
var stringTables = (IResourceDirectory) image.Resources.Entries.First(e => e.Id == (int) ResourceType.String);
var stringEntry = (IResourceDirectory) stringTables.Entries.First(e => e.Id == 251); // ID
var dataEntry = (IResourceData) stringEntry.Entries.First(e => e.Id == 1033); // Lang ID

// Build up new string contents
const string newString = " Preview-Version";
using var stream = new MemoryStream();
var writer = new BinaryStreamWriter(stream);
writer.WriteUInt16((ushort) newString.Length);
writer.WriteBytes(Encoding.Unicode.GetBytes(newString));
writer.Align(4);

// Swap old data with new data (NOTE: in this example it erases any other string in this block as well, so in your 
// implementation you might want to change this slightly).
dataEntry.Contents = new DataSegment(stream.ToArray());

// Swap contents of original .rsrc section with new resource directory buffer.
var rsrc = file.Sections.First(s => s.Name == ".rsrc");
var newContents = new ResourceDirectoryBuffer();
newContents.AddDirectory(image.Resources);
rsrc.Contents = newContents;

// Update data directory in optional header.
file.AlignSections();
file.OptionalHeader.DataDirectories[(int) DataDirectoryIndex.ResourceDirectory] =
    new DataDirectory(newContents.DirectoryTable.Rva, newContents.DirectoryTable.GetPhysicalSize());

// Save.
file.Write("basebrd.dll.mui.patched");

See the following docs for more info on these classes:

@laomms
Copy link
Author

laomms commented Nov 21, 2021

Thanks @Washi1337, it's a great library.

I've seen a similar project(https://github.com/kohoutech/Resourcery) where able to list detailed resource information. Unfortunately it doesn't have to save the modified resource function.

@Washi1337
Copy link
Owner

I am not familiar with Resourcery, but it seems like it is a program with the sole purpose of viewing/editing resources of a PE file.

Both projects probably have pros and cons and it depends on what you need on whether you want to go with AsmResolver or a project such as Resourcery. The rich feature set of AsmResolver may make "simple" tasks (such as editing a single string entry) a bit more complicated than just calling a single function as other libraries might provide. On the other hand, the goal of AsmResolver is not to be just a resources editor, but an entire PE editor, giving you more freedom in what you can do and cannot do.

@laomms
Copy link
Author

laomms commented Nov 21, 2021

Thanks very much. now I can search for byte arrays and replace them.

            var stringTables = (IResourceDirectory)image.Resources.Entries.First(e => e.Id == (int)ResourceType.String);   
            var stringEntry = (IResourceDirectory)stringTables.Entries.First(e => e.Id == 251); // ID   
            var dataEntry = (IResourceData)stringEntry.Entries.First(e => e.Id == 1033); // Lang ID   
            byte[] content = ((AsmResolver.DataSegment)dataEntry.Contents).Data;   
            List<string> stringlist = GetStringResource(251, content);
private List<string> GetStringResource(int name, byte[] bytesIn)
{
		List<string> result = new List<string>();
		MemoryStream ms = new MemoryStream();
		if (bytesIn.Length > 0)
		{
			BinaryWriter writer = new BinaryWriter(ms);
			char[] OldBinary = Encoding.Unicode.GetString(bytesIn).ToCharArray();
			int offset = 0;
			for (int index = 0; index <= 15; index++)
			{
				if (Convert.ToInt32(OldBinary[offset]) != 0)
				{
					int length = OldBinary[offset]; 
					offset += 1;
					if (length > 0)
					{
						int stringID = (name - 1) * 16 + index;
						writer.Write(stringID);
						writer.Write(length);
						int inner = offset;
						while (inner < offset + length)
						{
							writer.Write(OldBinary[inner]);
							inner += 1;
						}
					}
					offset += length;
				}
				else
				{
					offset += 1;
				}
			}
			ms.Position = 0;
		}

		if (ms != null && ms.Length > 0)
		{
			BinaryReader reader = new BinaryReader(ms);
			while (ms.Position < ms.Length)
			{
				int stringID = reader.ReadInt32();
				int length = reader.ReadInt32();
				char[] buff = reader.ReadChars(length);
				Console.WriteLine(stringID.ToString() + ":" + new string(buff));
				result.Add(new string(buff));
			}
		}
		return result;
	}

@Washi1337
Copy link
Owner

Nice, does that mean your issue has been resolved ?

@laomms
Copy link
Author

laomms commented Nov 22, 2021

yes, thank you very much!

@laomms
Copy link
Author

laomms commented Nov 23, 2021

  private byte[] WriteStringResource(int name, byte[] bytesIn, Dictionary<string, string> UpdateString)
  {
		MemoryStream ms = new MemoryStream();
		if (bytesIn.Length > 0)
		{
			BinaryWriter writer = new BinaryWriter(ms, Encoding.Unicode);
			char[] OldBinary = Encoding.Unicode.GetString(bytesIn).ToCharArray();
			int oldOffset = 0;
			int newOffset = 0;
			for (int index = 0; index <= 15; index++)
			{
				try
				{
					if (Convert.ToInt32(OldBinary[oldOffset]) != 0)
					{
						int length = OldBinary[oldOffset];
						oldOffset += 1;
						newOffset += 1;
						if (length > 0)
						{
							int stringID = (name - 1) * 16 + index;
							if (UpdateString.ContainsKey(stringID.ToString()))
							{
								writer.Write((short)(UpdateString[stringID].Length));
								writer.Write(Encoding.Unicode.GetBytes(UpdateString[stringID]));
								newOffset += UpdateString[stringID].Length;
							}
							else
							{
								writer.Write((short)length);
								int inner = oldOffset;
								while (inner < oldOffset + length)
								{
									writer.Write(OldBinary[inner]);
									inner += 1;
								}
							}
							oldOffset += length;
						}
					}
					else
					{
						writer.Write(new byte[] {0, 0});
						oldOffset += 1;
						newOffset += 1;
					}
				}
				catch (Exception ex)
				{

				}
			}
			ms.Position = 0;
		}
		return ms.ToArray();
	}

Dictionary<string, string> UpdateString = new Dictionary<string, string>()
{
{"4000", "aaa"},
{"4001", "bbb"}
};
byte[] bytesRet = WriteStringResource(251, bytes, UpdateString);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pe Issues related to AsmResolver.PE question win32resources Issues related to AsmResolver.PE.Win32Resources
Projects
None yet
Development

No branches or pull requests

2 participants