WerWolv / ImHex-Patterns

Hex patterns, include patterns and magic files for the use with the ImHex Hex Editor
https://github.com/WerWolv/ImHex
GNU General Public License v2.0
640 stars 168 forks source link

Win32 / MFC support #264

Open maaarghk opened 2 months ago

maaarghk commented 2 months ago

I'm playing about with ImHex as a tool to reverse a program written using MFC which stores files using CArchive. There is a very useful post about this here.

I've written some structs that I think would probably be quite useful to add to the library, since MFC is obviously a very widely used tool and a lot of applications out there will use CArchive to store files.

Here's an example for strings.

struct MFCString {
    u8 length;
    if (length < 255) {
        char data[length];
    } else if (length == 255) {
        u16 nextLength;
        if (nextLength == 65535) {
            u32 maxLength;
            char data[maxLength];
        } else {
            char data[nextLength];
        }
    }
} [[sealed, format("std::string::impl::format_string"), transform("std::string::impl::format_string")]];

and for OleDateTime...

enum MFCOleDateTimeStatus : u32 {
    VALID = 0x00,
    INVALID = 0x01,
    NULL = 0x02
};

struct MFCOleDateTime {
    MFCOleDateTimeStatus status;
    double timestamp;
} [[sealed, format("formatOleTime")]];

fn transformOleTime(MFCOleDateTime oleDT)
{
    return std::time::to_utc((oleDT.timestamp - 25569.0) * 24.0 * 3600.0);
};

fn formatOleTime(MFCOleDateTime oleDT)
{
    std::time::Time newTime = transformOleTime(oleDT);
    return std::time::format(newTime);
};

I'm happy to contribute using MRs but wasn't sure where to put these organisationally. My gut says that std::string::MFCString and std::time::MFCOleDateTime are heading in the direction of polluting every single file with MFC stuff, although I see DOSTime sort of works like that.

Maybe a good option would be for you to add the above yourself in a place you see fit, and if/when I have more to contribute, I'll follow along with your organisational choices?

WerWolv commented 2 months ago

Hey, thank you very much! These can absolutely just be added to the string and time library without issue. Everything in the standard library is in its own namespace either way and will not pollute any user code. I can add them to the relevant files if you want me to

maaarghk commented 2 months ago

I've taken a look but I think it'd be best if you add these ones and I just copy-by-example if/when I write any other implementations. Wasn't sure what to do about tests - I'm working with a proprietary app and don't have any files / patterns I can share to test it, given a bit more time I'm sure I could find that some core windows app is written using MFC and find a public domain pattern and file to add... in the mean time maybe this is helpful?

import std.time;
import std.string;

/**
    A serialised MFC CString
**/
struct MFCString {
    // Ref: https://github.com/adzm/atlmfc/blob/ea3ab8e4dee965905ae14f34d04b44ad1027827b/src/mfc/arccore.cpp#L28
    // TODO - support unicode strings / 8 byte lengths?
    u8 length;
    if (length < 255) {
        char data[length];
    } else if (length == 255) {
        u16 nextLength;
        if (nextLength == 65535) {
            u32 maxLength;
            char data[maxLength];
        } else {
            char data[nextLength];
        }
    }
} [[sealed, format("std::string::impl::format_string"), transform("std::string::impl::format_string")]];

enum MFCOleDateTimeStatus : u32 {
    VALID = 0x00,
    INVALID = 0x01,
    NULL = 0x02
};

/**
    A serialised MFC COleDateTime
**/
struct MFCOleDateTime {
    MFCOleDateTimeStatus status;
    double timestamp;
} [[sealed, format("format_ole_time")]];

/**
    Convert a serialised MFC COleDateTime to a UTC time
**/
fn ole_time_to_utc(MFCOleDateTime ole_dt)
{
    return std::time::to_utc((ole_dt.timestamp - 25569.0) * 24.0 * 3600.0);
};

fn format_ole_time(MFCOleDateTime ole_dt)
{
    std::time::Time time = ole_time_to_utc(ole_dt);
    return std::time::format(time);
};

struct MFCDoc {
    MFCString stuff;
    MFCOleDateTime stuff2;
    MFCString stuff3;
    MFCOleDateTime stuff4;
};

MFCDoc doc @0x00;

The following bytes (de-64 obv):

BlJhdGUgMQAAAACrqqqqYszmQP8qAWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc
3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3
h5ejEyMzQ1Njc4OTBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHF
yc3R1dgoNCg1hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d2N5ejEyMzQ1Njc4OTAgYWJjZGVmZ2QNCjAxMjM0NTY3ODkw
LT0rX2FiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6YWJjZGVmZ2hpamtsbW5vcHFyc3R1DQoAAAAAimdFIzXP5UA=

should result in this output

{
    "doc": {
        "stuff": "Rate%201",
        "stuff2": "Sun%20Oct%2031%2002%3A00%3A00%202027",
        "stuff3": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz01234567890abcdefghijklmnopqrstuv%0A%0D%0A%0Dabcdefghijklmnopqrstuvwcyz1234567890%20abcdefgd%0D%0A01234567890-%3D%2B_abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu%0D%0A",
        "stuff4": "Thu%20Apr%2014%2015%3A51%3A12%202022"
    }
}