Native Linux App to Edit Win32 Pe Like Reshacker

Native Linux app to edit Win32 PE like ResHacker

i586-mingw32msvc-windres as a part of the mingw package.

NAME
windres - manipulate Windows resources.

Command line only, but you can check the source as binutils are free (as in speech).

C library to read EXE version from Linux?

The version of the file is in the VS_FIXEDFILEINFO struct, but you have to find it into the executable data. There are two ways of doing what you want:

  1. Search for the VERSION_INFO signature in the file and read the VS_FIXEDFILEINFO struct directly.
  2. Find the .rsrc section, parse the resource tree, find the RT_VERSION resource, parse it and extract the VS_FIXEDFILEINFO data.

The first one is easier, but susceptible to find the signature by chance in the wrong place. Moreover, the other data you ask for (product name, description, etc.) are not in this structure, so I'll try to explain how to obtain the data the hard way.

The PE format is a bit convoluted so I'm pasting the code piece by piece, with comments, and with minimum error checking. I'll write a simple function that dumps the data to the standard output. Writing it as a proper function is left as an exercise to the reader :)

Note that I will be using offsets in the buffer instead of mapping the structs directly to avoid portability problems related to the alignment or padding of the struct fields. Anyway, I've annotated the type of the structs used (see include file winnt.h for details).

First a few useful declarations, they should be self-explanatory:

typedef uint32_t DWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;

#define READ_BYTE(p) (((unsigned char*)(p))[0])
#define READ_WORD(p) ((((unsigned char*)(p))[0]) | ((((unsigned char*)(p))[1]) << 8))
#define READ_DWORD(p) ((((unsigned char*)(p))[0]) | ((((unsigned char*)(p))[1]) << 8) | \
((((unsigned char*)(p))[2]) << 16) | ((((unsigned char*)(p))[3]) << 24))

#define PAD(x) (((x) + 3) & 0xFFFFFFFC)

Then a function that finds the Version resource in the executable image (no size checks).

const char *FindVersion(const char *buf)
{

The first structure in the EXE is the MZ header (for compatibility with MS-DOS).

    //buf is a IMAGE_DOS_HEADER
if (READ_WORD(buf) != 0x5A4D) //MZ signature
return NULL;

The only field interesting in the MZ header is the offset of the PE header. The PE header is the real thing.

    //pe is a IMAGE_NT_HEADERS32
const char *pe = buf + READ_DWORD(buf + 0x3C);
if (READ_WORD(pe) != 0x4550) //PE signature
return NULL;

Actually, the PE header is quite boring, we want the COFF header, that have all the symbolic data.

    //coff is a IMAGE_FILE_HEADER
const char *coff = pe + 4;

We just need the following fields from this one.

    WORD numSections = READ_WORD(coff + 2);
WORD optHeaderSize = READ_WORD(coff + 16);
if (numSections == 0 || optHeaderSize == 0)
return NULL;

The optional header is actually mandatory in an EXE and it is just after the COFF. The magic is different for 32 and 64 bits Windows. I'm assuming 32 bits from here on.

    //optHeader is a IMAGE_OPTIONAL_HEADER32
const char *optHeader = coff + 20;
if (READ_WORD(optHeader) != 0x10b) //Optional header magic (32 bits)
return NULL;

Here comes the interesting part: we want to find the resources section. It has two parts: 1. the section data, 2. the section metadata.

The data location is in a table at the end of the optional header, and each section has a well known index in this table. Resource section is in index 2, so we obtain the virtual address (VA) of the resource section with:

    //dataDir is an array of IMAGE_DATA_DIRECTORY
const char *dataDir = optHeader + 96;
DWORD vaRes = READ_DWORD(dataDir + 8*2);

//secTable is an array of IMAGE_SECTION_HEADER
const char *secTable = optHeader + optHeaderSize;

To get the section metadata we need to iterate the section table looking for a section named .rsrc.

    int i;
for (i = 0; i < numSections; ++i)
{
//sec is a IMAGE_SECTION_HEADER*
const char *sec = secTable + 40*i;
char secName[9];
memcpy(secName, sec, 8);
secName[8] = 0;

if (strcmp(secName, ".rsrc") != 0)
continue;

The section struct has two relevant members: the VA of the section and the offset of the section into the file (also the size of the section, but I'm not checking it!):

        DWORD vaSec = READ_DWORD(sec + 12);
const char *raw = buf + READ_DWORD(sec + 20);

Now the offset in the file that correspond to the vaRes VA we got before is easy.

        const char *resSec = raw + (vaRes - vaSec);

This is a pointer to the resource data. All the individual resources are set up in the form of a tree, with 3 levels: 1) type of resource, 2) identifier of resource, 3) language of resource. For the version we will get the very first one of the correct type.

First, we have a resource directory (for the type of resource), we get the number of entries in the directory, both named and unnamed and iterate:

        WORD numNamed = READ_WORD(resSec + 12);
WORD numId = READ_WORD(resSec + 14);

int j;
for (j = 0; j < numNamed + numId; ++j)
{

For each resource entry we get the type of the resource and discard it if it is not the RT_VERSION constant (16).

            //resSec is a IMAGE_RESOURCE_DIRECTORY followed by an array
// of IMAGE_RESOURCE_DIRECTORY_ENTRY
const char *res = resSec + 16 + 8 * j;
DWORD name = READ_DWORD(res);
if (name != 16) //RT_VERSION
continue;

If it is a RT_VERSION we get to the next resource directory in the tree:

            DWORD offs = READ_DWORD(res + 4);
if ((offs & 0x80000000) == 0) //is a dir resource?
return NULL;
//verDir is another IMAGE_RESOURCE_DIRECTORY and
// IMAGE_RESOURCE_DIRECTORY_ENTRY array
const char *verDir = resSec + (offs & 0x7FFFFFFF);

And go on to the next directory level, we don't care about the id. of this one:

            numNamed = READ_WORD(verDir + 12);
numId = READ_WORD(verDir + 14);
if (numNamed == 0 && numId == 0)
return NULL;
res = verDir + 16;
offs = READ_DWORD(res + 4);
if ((offs & 0x80000000) == 0) //is a dir resource?
return NULL;

The third level has the language of the resource. We don't care either, so just grab the first one:

            //and yet another IMAGE_RESOURCE_DIRECTORY, etc.
verDir = resSec + (offs & 0x7FFFFFFF);
numNamed = READ_WORD(verDir + 12);
numId = READ_WORD(verDir + 14);
if (numNamed == 0 && numId == 0)
return NULL;
res = verDir + 16;
offs = READ_DWORD(res + 4);
if ((offs & 0x80000000) != 0) //is a dir resource?
return NULL;
verDir = resSec + offs;

And we get to the real resource, well, actually a struct that contains the location and size of the real resource, but we don't care about the size.

            DWORD verVa = READ_DWORD(verDir);

That's the VA of the version resouce, that is converted into a pointer easily.

            const char *verPtr = raw + (verVa - vaSec);
return verPtr;

And done! If not found return NULL.

        }
return NULL;
}
return NULL;
}

Now that the version resource is found, we have to parse it. It is actually a tree (what else) of pairs "name" / "value". Some values are well known and that's what you are looking for, just do some test and you will find out which ones.

NOTE: All strings are stored in UNICODE (UTF-16) but my sample code does the dumb conversion into ASCII. Also, no checks for overflow.

The function takes the pointer to the version resource and the offset into this memory (0 for starters) and returns the number of bytes analyzed.

int PrintVersion(const char *version, int offs)
{

First of all the offset have to be a multiple of 4.

    offs = PAD(offs);

Then we get the properties of the version tree node.

    WORD len    = READ_WORD(version + offs);
offs += 2;
WORD valLen = READ_WORD(version + offs);
offs += 2;
WORD type = READ_WORD(version + offs);
offs += 2;

The name of the node is a Unicode zero-terminated string.

    char info[200];
int i;
for (i=0; i < 200; ++i)
{
WORD c = READ_WORD(version + offs);
offs += 2;

info[i] = c;
if (!c)
break;
}

More padding, if neccesary:

    offs = PAD(offs);

If type is not 0, then it is a string version data.

    if (type != 0) //TEXT
{
char value[200];
for (i=0; i < valLen; ++i)
{
WORD c = READ_WORD(version + offs);
offs += 2;
value[i] = c;
}
value[i] = 0;
printf("info <%s>: <%s>\n", info, value);
}

Else, if the name is VS_VERSION_INFO then it is a VS_FIXEDFILEINFO struct. Else it is binary data.

    else
{
if (strcmp(info, "VS_VERSION_INFO") == 0)
{

I'm just printing the version of the file and product, but you can find the other fields of this struct easily. Beware of the mixed endian order.

            //fixed is a VS_FIXEDFILEINFO
const char *fixed = version + offs;
WORD fileA = READ_WORD(fixed + 10);
WORD fileB = READ_WORD(fixed + 8);
WORD fileC = READ_WORD(fixed + 14);
WORD fileD = READ_WORD(fixed + 12);
WORD prodA = READ_WORD(fixed + 18);
WORD prodB = READ_WORD(fixed + 16);
WORD prodC = READ_WORD(fixed + 22);
WORD prodD = READ_WORD(fixed + 20);
printf("\tFile: %d.%d.%d.%d\n", fileA, fileB, fileC, fileD);
printf("\tProd: %d.%d.%d.%d\n", prodA, prodB, prodC, prodD);
}
offs += valLen;
}

Now do the recursive call to print the full tree.

    while (offs < len)
offs = PrintVersion(version, offs);

And some more padding before returning.

    return PAD(offs);
}

Finally, as a bonus, a main function.

int main(int argc, char **argv)
{
struct stat st;
if (stat(argv[1], &st) < 0)
{
perror(argv[1]);
return 1;
}

char *buf = malloc(st.st_size);

FILE *f = fopen(argv[1], "r");
if (!f)
{
perror(argv[1]);
return 2;
}

fread(buf, 1, st.st_size, f);
fclose(f);

const char *version = FindVersion(buf);
if (!version)
printf("No version\n");
else
PrintVersion(version, 0);
return 0;
}

I've tested it with a few random EXEs and it seems to work just fine.

How to change Eclipse RCP product exe properties

You are misreading the bugzilla discussion, as that is only possible by self-compiling the eclipse launcher source (with a modified rc file).

However, there is an alternative: You can patch the launcher executable manually (using any kind of resource editor) and then put it into your product as root file. This works with tycho, I use that approach. The drawback is of course that this executable will not be updated, if you change branding details in the product definition file.

Alternatively to a root file you could also overwrite the launcher executable during an installation creation (outside of Eclipse). E.g. we create an NSIS installer around an RCP app and can also modify it there.

How can I convert a VBScript to an executable (EXE) file?

There is no way to convert a VBScript (.vbs file) into an executable (.exe file) because VBScript is not a compiled language. The process of converting source code into native executable code is called "compilation", and it's not supported by scripting languages like VBScript.

Certainly you can add your script to a self-extracting archive using something like WinZip, but all that will do is compress it. It's doubtful that the file size will shrink noticeably, and since it's a plain-text file to begin with, it's really not necessary to compress it at all. The only purpose of a self-extracting archive is that decompression software (like WinZip) is not required on the end user's computer to be able to extract or "decompress" the file. If it isn't compressed in the first place, this is a moot point.

Alternatively, as you mentioned, there are ways to wrap VBScript code files in a standalone executable file, but these are just wrappers that automatically execute the script (in its current, uncompiled state) when the user double-clicks on the .exe file. I suppose that can have its benefits, but it doesn't sound like what you're looking for.

In order to truly convert your VBScript into an executable file, you're going to have to rewrite it in another language that can be compiled. Visual Basic 6 (the latest version of VB, before the .NET Framework was introduced) is extremely similar in syntax to VBScript, but does support compiling to native code. If you move your VBScript code to VB 6, you can compile it into a native executable. Running the .exe file will require that the user has the VB 6 Run-time libraries installed, but they come built into most versions of Windows that are found now in the wild.

Alternatively, you could go ahead and make the jump to Visual Basic .NET, which remains somewhat similar in syntax to VB 6 and VBScript (although it won't be anywhere near a cut-and-paste migration). VB.NET programs will also compile to an .exe file, but they require the .NET Framework runtime to be installed on the user's computer. Fortunately, this has also become commonplace, and it can be easily redistributed if your users don't happen to have it. You mentioned going this route in your question (porting your current script in to VB Express 2008, which uses VB.NET), but that you were getting a lot of errors. That's what I mean about it being far from a cut-and-paste migration. There are some huge differences between VB 6/VBScript and VB.NET, despite some superficial syntactical similarities. If you want help migrating over your VBScript, you could post a question here on Stack Overflow. Ultimately, this is probably the best way to do what you want, but I can't promise you that it will be simple.



Related Topics



Leave a reply



Submit