/*
* PERWAPI - An API for Reading and Writing PE Files
*
* Copyright (c) Diane Corney, Queensland University of Technology, 2004-2010.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the PERWAPI Copyright as included with this
* distribution in the file PERWAPIcopyright.rtf.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY as is explained in the copyright notice.
*
* The author may be contacted at d.corney@qut.edu.au
*
* Version Date: 26/01/07
*
* Contributions Made By:
*
* Douglas Stockwell - Developed support for PDB files.
* Andrew Bacon - Integrated PDB file support and developed automatic
* stack depth calculations.
*
*/
// The conditional compilation on the CORAPI symbol has been
// deleted from this version. Go back to version 1 in the QUT SVN
// repository to get the conditional code if needed.
using System;
using System.IO;
using System.Collections;
using System.Text;
using System.Diagnostics;
using System.Reflection; // For the assembly attributes
//
// The assembly version is managed manually here.
// 1.1.* are versions with the PDB handling built
// Version 1.1.1+ uses System.Security.Crytography
// to provide public key token methods
//
namespace QUT.PERWAPI {
internal class MetaDataTables {
private TableRow[][] tables;
internal MetaDataTables(TableRow[][] tabs) {
tables = tabs;
}
internal MetaDataElement GetTokenElement(uint token) {
uint tabIx = (token & FileImage.TableMask) >> 24;
uint elemIx = (token & FileImage.ElementMask) - 1;
return (MetaDataElement)tables[tabIx][(int)elemIx];
}
}
/**************************************************************************/
// Streams for PE File Reader
/**************************************************************************/
///
/// Stream in the Meta Data (#Strings, #US, #Blob and #GUID)
///
///
internal class MetaDataInStream : BinaryReader {
//protected bool largeIx = false;
protected byte[] data;
public MetaDataInStream(byte[] streamBytes)
: base(new MemoryStream(streamBytes)) {
data = streamBytes;
}
public uint ReadCompressedNum() {
//int pos = (int)BaseStream.Position;
//Console.WriteLine("Position = " + BaseStream.Position);
byte b = ReadByte();
//pos++;
uint num = 0;
if (b <= 0x7F) {
num = b;
}
else if (b >= 0xC0) {
num = (uint)(((b - 0xC0) << 24) + (ReadByte() << 16) + (ReadByte() << 8) + ReadByte());
}
else { // (b >= 0x80) && (b < 0xC0)
num = (uint)((b - 0x80) << 8) + ReadByte();
}
return num;
}
public int ReadCompressedInt() {
// This code is based on a revised version of the
// (incorrect) ECMA-335 spec which clarifies the
// encoding of array lower bounds. (kjg 2008-Feb-22 )
//
uint rawBits = ReadCompressedNum();
uint magnitude = (rawBits >> 1);
if ((rawBits & 1) != 1)
return (int)magnitude;
else if (magnitude <= 0x3f)
return (int)(magnitude | 0xffffffc0);
else if (magnitude <= 0x1fff)
return (int)(magnitude | 0xffffe000);
else
return (int)(magnitude | 0xf0000000);
}
internal bool AtEnd() {
long pos = BaseStream.Position;
long len = BaseStream.Length;
//if (pos >= len-1)
// Console.WriteLine("At end of stream");
return BaseStream.Position == BaseStream.Length - 1;
}
internal byte[] GetBlob(uint ix) {
if (ix == 0) return new byte[0];
BaseStream.Seek(ix, SeekOrigin.Begin);
//Console.WriteLine("Getting blob size at index " + buff.GetPos());
//if (Diag.CADiag) Console.WriteLine("Getting blob size at " + (BaseStream.Position+PEReader.blobStreamStartOffset));
uint bSiz = ReadCompressedNum();
//byte[] blobBytes = new byte[ReadCompressedNum()];
//if (Diag.CADiag) Console.WriteLine("Blob size = " + bSiz);
byte[] blobBytes = new byte[bSiz];
for (int i = 0; i < blobBytes.Length; i++) {
blobBytes[i] = ReadByte();
}
return blobBytes;
}
internal byte[] GetBlob(uint ix, int len) {
//Console.WriteLine("Getting blob size at index " + buffer.GetPos());
byte[] blobBytes = new byte[len];
for (int i = 0; i < len; i++) {
blobBytes[i] = data[ix++];
}
return blobBytes;
}
internal string GetString(uint ix) {
uint end;
for (end = ix; data[end] != '\0'; end++) ;
char[] str = new char[end - ix];
for (int i = 0; i < str.Length; i++) {
str[i] = (char)data[ix + i];
}
return new string(str, 0, str.Length);
}
internal string GetBlobString(uint ix) {
if (ix == 0) return "";
BaseStream.Seek(ix, SeekOrigin.Begin);
return GetBlobString();
}
internal string GetBlobString() {
uint strLen = ReadCompressedNum();
char[] str = new char[strLen];
uint readpos = (uint)this.BaseStream.Position;
for (int i = 0; i < strLen; i++) {
str[i] = ReadChar();
uint newpos = (uint)this.BaseStream.Position;
if (newpos > readpos + 1)
strLen -= newpos - (readpos + 1);
readpos = newpos;
}
return new string(str, 0, (int)strLen);
}
internal void GoToIndex(uint ix) {
BaseStream.Seek(ix, SeekOrigin.Begin);
}
}
/**************************************************************************/
internal class MetaDataStringStream : BinaryReader {
//BinaryReader br;
internal MetaDataStringStream(byte[] bytes)
: base(new MemoryStream(bytes), Encoding.Unicode) {
//br = new BinaryReader(new MemoryStream(bytes)/*,Encoding.Unicode*/);
}
private uint GetStringLength() {
uint b = ReadByte();
uint num = 0;
if (b <= 0x7F) {
num = b;
}
else if (b >= 0xC0) {
num = (uint)(((b - 0xC0) << 24) + (ReadByte() << 16) + (ReadByte() << 8) + ReadByte());
}
else { // (b >= 0x80) && (b < 0xC0)
num = (uint)((b - 0x80) << 8) + ReadByte();
}
return num;
}
internal string GetUserString(uint ix) {
BaseStream.Seek(ix, SeekOrigin.Begin);
uint strLen = GetStringLength() / 2;
char[] strArray = new char[strLen];
for (int i = 0; i < strLen; i++) {
//strArray[i] = ReadChar(); // works for everett but not whidbey
strArray[i] = (char)ReadUInt16();
}
return new String(strArray);
}
}
/**************************************************************************/
// Streams for generated MetaData
/**************************************************************************/
///
/// Stream in the generated Meta Data (#Strings, #US, #Blob and #GUID)
///
internal class MetaDataStream : BinaryWriter {
private static readonly uint StreamHeaderSize = 8;
private static uint maxSmlIxSize = 0xFFFF;
private uint start = 0;
uint size = 0, tide = 1;
bool largeIx = false;
uint sizeOfHeader;
internal char[] name;
Hashtable htable = new Hashtable();
internal MetaDataStream(char[] name, bool addInitByte)
: base(new MemoryStream()) {
if (addInitByte) { Write((byte)0); size = 1; }
this.name = name;
sizeOfHeader = StreamHeaderSize + (uint)name.Length;
}
internal MetaDataStream(char[] name, System.Text.Encoding enc, bool addInitByte)
: base(new MemoryStream(), enc) {
if (addInitByte) { Write((byte)0); size = 1; }
this.name = name;
sizeOfHeader = StreamHeaderSize + (uint)name.Length;
}
public uint Start {
get {
return start;
}
set {
start = value;
}
}
internal uint headerSize() {
// Console.WriteLine(name + " stream has headersize of " + sizeOfHeader);
return sizeOfHeader;
}
//internal void SetSize(uint siz) {
// size = siz;
//}
internal uint Size() {
return size;
}
internal bool LargeIx() {
return largeIx;
}
internal void WriteDetails() {
// Console.WriteLine(name + " - size = " + size);
}
internal uint Add(string str, bool prependSize) {
Object val = htable[str];
uint index = 0;
if (val == null) {
index = size;
htable[str] = index;
char[] arr = str.ToCharArray();
if (prependSize)
CompressNum((uint)arr.Length * 2 + 1);
Write(arr);
Write((byte)0);
size = (uint)Seek(0, SeekOrigin.Current);
}
else {
index = (uint)val;
}
return index;
}
internal uint Add(Guid guid) {
Write(guid.ToByteArray());
size = (uint)Seek(0, SeekOrigin.Current);
return tide++;
}
internal uint Add(byte[] blob) {
uint ix = size;
CompressNum((uint)blob.Length);
Write(blob);
size = (uint)Seek(0, SeekOrigin.Current);
return ix;
}
internal uint Add(long val, uint numBytes) {
uint ix = size;
Write((byte)numBytes);
switch (numBytes) {
case 1: Write((byte)val); break;
case 2: Write((short)val); break;
case 4: Write((int)val); break;
default: Write(val); break;
}
size = (uint)Seek(0, SeekOrigin.Current);
return ix;
}
internal uint Add(ulong val, uint numBytes) {
uint ix = size;
Write((byte)numBytes);
switch (numBytes) {
case 1: Write((byte)val); break;
case 2: Write((ushort)val); break;
case 4: Write((uint)val); break;
default: Write(val); break;
}
size = (uint)Seek(0, SeekOrigin.Current);
return ix;
}
internal uint Add(char ch) {
uint ix = size;
Write((byte)2); // size of blob to follow
Write(ch);
size = (uint)Seek(0, SeekOrigin.Current);
return ix;
}
internal uint Add(float val) {
uint ix = size;
Write((byte)4); // size of blob to follow
Write(val);
size = (uint)Seek(0, SeekOrigin.Current);
return ix;
}
internal uint Add(double val) {
uint ix = size;
Write((byte)8); // size of blob to follow
Write(val);
size = (uint)Seek(0, SeekOrigin.Current);
return ix;
}
private void CompressNum(uint val) {
if (val <= 0x7F) {
Write((byte)val);
}
else if (val <= 0x3FFF) {
byte b1 = (byte)((val >> 8) | 0x80);
byte b2 = (byte)(val & FileImage.iByteMask[0]);
Write(b1);
Write(b2);
}
else {
byte b1 = (byte)((val >> 24) | 0xC0);
byte b2 = (byte)((val & FileImage.iByteMask[2]) >> 16);
byte b3 = (byte)((val & FileImage.iByteMask[1]) >> 8); ;
byte b4 = (byte)(val & FileImage.iByteMask[0]);
Write(b1);
Write(b2);
Write(b3);
Write(b4);
}
}
private void QuadAlign() {
if ((size % 4) != 0) {
uint pad = 4 - (size % 4);
size += pad;
for (int i = 0; i < pad; i++) {
Write((byte)0);
}
}
}
internal void EndStream() {
QuadAlign();
if (size > maxSmlIxSize) {
largeIx = true;
}
}
internal void WriteHeader(BinaryWriter output) {
output.Write(start);
output.Write(size);
output.Write(name);
}
internal virtual void Write(BinaryWriter output) {
// Console.WriteLine("Writing " + name + " stream at " + output.Seek(0,SeekOrigin.Current) + " = " + start);
MemoryStream str = (MemoryStream)BaseStream;
output.Write(str.ToArray());
}
}
}