/* JSmooth: a VM wrapper toolkit for Windows Copyright (C) 2003 Rodrigo Reyes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * PEResourceDirectory.java * * Created on 2 aout 2003, 01:28 */ package net.charabia.jsmoothgen.pe; import java.io.IOException; import java.io.PrintStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.util.Iterator; import java.util.Vector; /** * @author Rodrigo */ public class PEResourceDirectory { /* typedef struct IMAGE_RESOURCE_DIRECTORY { uint32_t Characteristics; uint32_t TimeDateStamp; uint16_t MajorVersion; uint16_t MinorVersion; uint16_t NumberOfNamedEntries; uint16_t NumberOfIdEntries; } */ public class DataEntry { long OffsetToData; // To update at each change long Size; long CodePage; // never changed long Reserved; // never changed ByteBuffer Data; public DataEntry(ByteBuffer data) { this.Data = data; this.Size = data.capacity(); } public DataEntry(FileChannel chan, long offset) throws IOException { long orgpos = chan.position(); chan.position(PEResourceDirectory.this.m_offsetBase + offset); ByteBuffer buf = ByteBuffer.allocate(16); buf.order(ByteOrder.LITTLE_ENDIAN); chan.read(buf); buf.position(0); OffsetToData = buf.getInt(); Size = buf.getInt(); CodePage = buf.getInt(); Reserved = buf.getInt(); long datapos = PEResourceDirectory.this.m_master.PointerToRawData + (OffsetToData - PEResourceDirectory.this.m_master.VirtualAddress); Data = ByteBuffer.allocate((int)Size); Data.order(ByteOrder.LITTLE_ENDIAN); chan.position(datapos); chan.read(Data); Data.position(0); chan.position(orgpos); } public int diskSize() { int size = 16 + (int)this.Size; if ((size % 4) > 0) size += 4 - (size % 4); return size; } public void dump(PrintStream out, int level) { indent(level, out); out.println("OffsetToData=" + OffsetToData); indent(level, out); out.println("Size=" + Size); indent(level, out); out.println("CodePage=" + CodePage); indent(level, out); out.println("Reserved=" + Reserved); indent(level, out); out.print("Data={ "); for (int i = 0; i < this.Data.capacity(); i++) { out.print("" + Integer.toHexString((byte)Data.get()) + ","); } out.println(" }"); } private void indent(int level, PrintStream out) { for (int i = 0; i < level; i++) out.print(" "); } public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset, int dataOffset) { // System.out.println("Building Data Entry buffer @ " + buffer.position() + " (" + dataOffset + ")"); dataOffset = buffer.position() + 16; buffer.putInt((int)(dataOffset + virtualBaseOffset)); buffer.putInt((int)Size); buffer.putInt((int)CodePage); buffer.putInt((int)Reserved); Data.position(0); buffer.put(Data); dataOffset += Size; if ((dataOffset % 4) > 0) dataOffset += (4 - (dataOffset % 4)); return dataOffset; } public void setData(ByteBuffer data) { Data = data; Size = data.capacity(); } } public class ResourceEntry { int Id; String Name; ImageResourceDirectory Directory; DataEntry Data; public ResourceEntry(int id, DataEntry data) { this.Id = id; this.Data = data; } public ResourceEntry(String name, DataEntry data) { this.Name = name; this.Data = data; } public ResourceEntry(int id, ImageResourceDirectory dir) { this.Id = id; this.Directory = dir; } public ResourceEntry(String name, ImageResourceDirectory dir) { this.Name = name; this.Directory = dir; } public ResourceEntry(FileChannel chan) throws IOException { // System.out.println("Resource Entry Offset: " + chan.position()); ByteBuffer buf = ByteBuffer.allocate(8); buf.order(ByteOrder.LITTLE_ENDIAN); chan.read(buf); buf.position(0); long orgchanpos = chan.position(); int val = buf.getInt(); long offsetToData = buf.getInt(); // System.out.println("Entry: Val=" + val); // System.out.println(" Off=" + offsetToData); if (val < 0) { val &= 0x7FFFFFFF; Name = extractStringAt(chan, val); Id = -1; // System.out.println(" String at " + val + " = " + Name); } else { Id = val; } if (offsetToData < 0) { offsetToData &= 0x7FFFFFFF; long orgpos = chan.position(); chan.position(PEResourceDirectory.this.m_offsetBase + offsetToData); Directory = new PEResourceDirectory.ImageResourceDirectory(chan); chan.position(orgpos); } else { Data = new DataEntry(chan, offsetToData); } } public String extractStringAt(FileChannel chan, int offset) throws IOException { long orgchanpos = chan.position(); chan.position(PEResourceDirectory.this.m_offsetBase + offset); ByteBuffer sizebuf = ByteBuffer.allocate(2); sizebuf.order(ByteOrder.LITTLE_ENDIAN); chan.read(sizebuf); sizebuf.position(0); int size = sizebuf.getShort(); ByteBuffer buffer = ByteBuffer.allocate(size * 2); buffer.order(ByteOrder.LITTLE_ENDIAN); chan.read(buffer); buffer.position(0); StringBuffer buf = new StringBuffer(size); for (int i = 0; i < size; i++) { int c = buffer.getShort(); buf.append((char)c); } chan.position(orgchanpos); return buf.toString(); } public int diskSize() { int size = 8; if (Name != null) size += (Name.length() * 2) + 2; if (Directory != null) size += Directory.diskSize(); else if (Data != null) size += Data.diskSize(); if ((size % 4) > 0) size += 4 - (size % 4); return size; } public void dump(PrintStream out, int level) { indent(level, out); if (this.Name != null) out.println("Name=" + Name); else out.println("Id=#" + Id); indent(level, out); if (this.Directory != null) { out.println("ENTRY: DIRECTORY POINTER"); this.Directory.dump(out, level + 1); } else { out.println("ENTRY: DATA ENTRY"); Data.dump(out, level + 1); } } private void indent(int level, PrintStream out) { for (int i = 0; i < level; i++) out.print(" "); } public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset, int dataOffset) { // System.out.println("Building Resource Entry buffer " + Name + "/" + Id + " @ " + buffer.position() + " (" + dataOffset + ")"); if (Name != null) { buffer.putInt((int)(dataOffset | 0x80000000)); int stringoffset = dataOffset; ByteBuffer strbuf = ByteBuffer.allocate(Name.length() * 2 + 2); strbuf.order(ByteOrder.LITTLE_ENDIAN); strbuf.putShort((short)Name.length()); for (int i = 0; i < Name.length(); i++) { strbuf.putShort((short)Name.charAt(i)); } strbuf.position(0); long oldpos = buffer.position(); buffer.position(dataOffset); buffer.put(strbuf); dataOffset += Name.length() * 2 + 2; if ((dataOffset % 4) != 0) dataOffset += 4 - (dataOffset % 4); buffer.position((int) oldpos); } else { buffer.putInt(Id); } if (Directory != null) { buffer.putInt((int)(dataOffset | 0x80000000)); int oldpos = buffer.position(); buffer.position(dataOffset); int dirsize = Directory.buildBuffer(buffer, virtualBaseOffset); dataOffset = dirsize; buffer.position(oldpos); } else if (Data != null) { buffer.putInt(dataOffset); int oldpos = buffer.position(); buffer.position(dataOffset); dataOffset = Data.buildBuffer(buffer, virtualBaseOffset, dataOffset); buffer.position(oldpos); } else { throw new RuntimeException("Directory and Data are both null!"); } return dataOffset; } } public class ImageResourceDirectory { long Characteristics; // uint32_t long TimeDateStamp; // uint32_t int MajorVersion; // uint16_t int MinorVersion; // uint16_t int NumberOfNamedEntries; // uint16_t int NumberOfIdEntries; // uint16_t Vector NamedEntries = new Vector(); Vector IdEntries = new Vector(); public ImageResourceDirectory() { } public ImageResourceDirectory(FileChannel chan) throws IOException { ByteBuffer header = ByteBuffer.allocate(16); header.order(ByteOrder.LITTLE_ENDIAN); chan.read(header); header.position(0); Characteristics = header.getInt(); TimeDateStamp = header.getInt(); MajorVersion = header.getShort(); MinorVersion = header.getShort(); NumberOfNamedEntries = header.getShort(); NumberOfIdEntries = header.getShort(); for (int i = 0; i < NumberOfNamedEntries; i++) { ResourceEntry re = new ResourceEntry(chan); NamedEntries.add(re); } for (int i = 0; i < NumberOfIdEntries; i++) { ResourceEntry re = new ResourceEntry(chan); IdEntries.add(re); } } public void addNamedEntry(ResourceEntry entry) { this.NamedEntries.add(entry); } public void addIdEntry(ResourceEntry entry) { this.IdEntries.add(entry); } public void addEntry(ResourceEntry entry) { if (entry.Name != null) addNamedEntry(entry); else addIdEntry(entry); } public void dump(PrintStream out, int level) { indent(level, out); out.println("Directory: "); indent(level, out); out.println("Characteristics=" + this.Characteristics); indent(level, out); out.println("TimeDateStamp=" + this.TimeDateStamp); indent(level, out); out.println("MajorVersion=" + this.MajorVersion); indent(level, out); out.println("MinorVersion=" + this.MinorVersion); indent(level, out); out.println("NumberOfNamedEntries=" + this.NumberOfNamedEntries); indent(level, out); out.println("NumberOfIdEntries=" + this.NumberOfIdEntries); indent(level, out); out.println("Named Entries:"); for (int i = 0; i < NumberOfNamedEntries; i++) { ResourceEntry re = (ResourceEntry)NamedEntries.get(i); re.dump(out, level + 1); } indent(level, out); out.println("Id Entries:"); for (int i = 0; i < NumberOfIdEntries; i++) { ResourceEntry re = (ResourceEntry)IdEntries.get(i); re.dump(out, level + 1); } } private void indent(int level, PrintStream out) { for (int i = 0; i < level; i++) out.print(" "); } public int diskSize() { int size = 16; for (int i = 0; i < this.NamedEntries.size(); i++) { ResourceEntry re = (ResourceEntry)NamedEntries.get(i); size += re.diskSize(); } for (int i = 0; i < this.IdEntries.size(); i++) { ResourceEntry re = (ResourceEntry)IdEntries.get(i); size += re.diskSize(); } if ((size % 4) > 0) size += 4 - (size % 4); return size; } public int buildBuffer(ByteBuffer buffer, long virtualBaseOffset) { // System.out.println("Building Directory Entry buffer @ " + buffer.position()); buffer.putInt((int)this.Characteristics); buffer.putInt((int)this.TimeDateStamp); buffer.putShort((short)this.MajorVersion); buffer.putShort((short)this.MinorVersion); buffer.putShort((short)this.NamedEntries.size()); buffer.putShort((short)this.IdEntries.size()); int dataOffset = buffer.position() + (NamedEntries.size() * 8) + (IdEntries.size() * 8); for (int i = 0; i < this.NamedEntries.size(); i++) { ResourceEntry re = (ResourceEntry)this.NamedEntries.get(i); dataOffset = re.buildBuffer(buffer, virtualBaseOffset, dataOffset); } for (int i = 0; i < this.IdEntries.size(); i++) { ResourceEntry re = (ResourceEntry)this.IdEntries.get(i); dataOffset = re.buildBuffer(buffer, virtualBaseOffset, dataOffset); } buffer.position(dataOffset); return dataOffset; } public ResourceEntry getResourceEntry(String name) { // If name == null, get the first entry in lexical // order. If no entry in lexical order, choose the // lowest integer id entry. if (name == null) { if (NamedEntries.size() > 0) { return (PEResourceDirectory.ResourceEntry)NamedEntries.get(0); } if (IdEntries.size() > 0) { return (PEResourceDirectory.ResourceEntry)IdEntries.get(0); } return null; } if ((name.length() > 0) && (name.charAt(0) == '#')) { try { String nb = name.substring(1); int i = Integer.parseInt(nb); return getResourceEntry(i); } catch (Exception exc) { exc.printStackTrace(); } } for (Iterator i = this.NamedEntries.iterator(); i.hasNext();) { ResourceEntry re = (ResourceEntry)i.next(); if (name.equals(re.Name)) { return re; } } return null; } public ResourceEntry getResourceEntry(int id) { for (Iterator i = this.IdEntries.iterator(); i.hasNext();) { ResourceEntry re = (ResourceEntry)i.next(); if (id == re.Id) { return re; } } return null; } } PESection m_master; PEFile m_file; long m_offsetBase; PEResourceDirectory.ImageResourceDirectory m_root; /** * Creates a new instance of PEResourceDirectory */ public PEResourceDirectory(PEFile file, PESection sect) throws IOException { m_master = sect; m_file = file; m_offsetBase = sect.PointerToRawData; init(); // System.out.println("--------\nTOTAL SIZE: " + m_root.diskSize()); // System.out.println("\n\n"); } public void init() throws IOException { // / System.out.println("RESOURCE INIT"); // System.out.println(" Offset: " + m_master.PointerToRawData); FileChannel chan = m_file.getChannel(); chan.position(m_master.PointerToRawData); PEResourceDirectory.ImageResourceDirectory dir = new PEResourceDirectory.ImageResourceDirectory(chan); // System.out.println("-----------------\nDUMP\n---------------"); m_root = dir; // dir.dump(System.out, 0); } public void dump(PrintStream out) { m_root.dump(out, 0); } public int size() { return m_root.diskSize(); } public ByteBuffer buildResource(long virtualBaseOffset) { // System.out.println("BUILDING RESOURCE / VIRTUAL: " + virtualBaseOffset); int resourceSize = m_root.diskSize(); ByteBuffer resbuf = ByteBuffer.allocate(resourceSize); resbuf.order(ByteOrder.LITTLE_ENDIAN); resbuf.position(0); m_root.buildBuffer(resbuf, virtualBaseOffset); return resbuf; } public PEResourceDirectory.ImageResourceDirectory getRoot() { return m_root; } public boolean replaceManifest(int resourceId, int langId, ByteBuffer data) { ResourceEntry catEntry = m_root.getResourceEntry(24); if ((catEntry != null) && (catEntry.Directory != null)) { ResourceEntry identEntry = catEntry.Directory.getResourceEntry(resourceId); if ((identEntry != null) && (identEntry.Directory != null)) { ResourceEntry langEntry = identEntry.Directory.getResourceEntry(langId); if ((langEntry != null) && (langEntry.Data != null)) { DataEntry dataslot = langEntry.Data; dataslot.setData(data); return true; } } } return false; } public boolean replaceResource(String catId, int resourceId, int langId, ByteBuffer data) { ResourceEntry catEntry = m_root.getResourceEntry(catId); if ((catEntry != null) && (catEntry.Directory != null)) { ResourceEntry identEntry = catEntry.Directory.getResourceEntry(resourceId); if ((identEntry != null) && (identEntry.Directory != null)) { ResourceEntry langEntry = identEntry.Directory.getResourceEntry(langId); if ((langEntry != null) && (langEntry.Data != null)) { DataEntry dataslot = langEntry.Data; dataslot.setData(data); return true; } } } return false; } public void addNewResource(String catId, String resourceId, String languageId, ByteBuffer data) { DataEntry dataEntry = new DataEntry(data); ResourceEntry languageEntry = buildResourceEntry(languageId, dataEntry); ImageResourceDirectory languageDir = new ImageResourceDirectory(); languageDir.TimeDateStamp = 0x3F2CCF64; languageDir.addEntry(languageEntry); ResourceEntry identEntry = buildResourceEntry(resourceId, languageDir); ImageResourceDirectory identDir = new ImageResourceDirectory(); identDir.TimeDateStamp = 0x3F2CCF64; identDir.addEntry(identEntry); ResourceEntry catEntry = buildResourceEntry(catId, identDir); m_root.addEntry(catEntry); } public DataEntry getData(String catId, String resourceId, String langId) { ResourceEntry catEntry = m_root.getResourceEntry(catId); if ((catEntry != null) && (catEntry.Directory != null)) { ResourceEntry identEntry = catEntry.Directory.getResourceEntry(resourceId); if ((identEntry != null) && (identEntry.Directory != null)) { ResourceEntry langEntry = identEntry.Directory.getResourceEntry(langId); if ((langEntry != null) && (langEntry.Data != null)) { DataEntry dataslot = langEntry.Data; return dataslot; } } } return null; } public ResourceEntry buildResourceEntry(String id, DataEntry data) { if ((id.length() > 1) && (id.charAt(0) == '#')) { int intid = Integer.parseInt(id.substring(1)); return new ResourceEntry(intid, data); } return new ResourceEntry(id, data); } public ResourceEntry buildResourceEntry(String id, ImageResourceDirectory dir) { if ((id.length() > 1) && (id.charAt(0) == '#')) { int intid = Integer.parseInt(id.substring(1)); return new ResourceEntry(intid, dir); } return new ResourceEntry(id, dir); } }