simple parsing of resources

This commit is contained in:
mrbesen 2023-01-11 22:35:19 +01:00
parent 2405fa9bc1
commit 69f7ed2a2e
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
8 changed files with 259 additions and 139 deletions

View File

@ -5,12 +5,12 @@
#include <memory>
struct RegisteredResource {
RegisteredResource(int i, const unsigned char* resourceStruct, const unsigned char* resourceName, const unsigned char* resourceData);
RegisteredResource(int version, const unsigned char* resourceStruct, const unsigned char* resourceName, const unsigned char* resourceData);
int i; // what is this? version? type? (always just 2?)
int version;
const unsigned char* resourceStruct;
const unsigned char* resourceName;
const unsigned char* resourceData;
};
std::map<uint64_t, std::shared_ptr<RegisteredResource>>* registeredResources = nullptr;
extern std::map<uint64_t, std::shared_ptr<RegisteredResource>>* registeredResources;

View File

@ -1,30 +0,0 @@
#pragma once
#include <QDir>
#include <QFileSystemModel>
#include <QVector>
class QFileInfo;
class QtResourceModel : public QAbstractItemModel {
public:
QtResourceModel( const QDir& root, QObject* parent = nullptr );
virtual ~QtResourceModel();
virtual QModelIndex index(int, int, const QModelIndex&) const override;
virtual QModelIndex parent(const QModelIndex&) const override;
virtual int rowCount(const QModelIndex&) const override;
virtual int columnCount(const QModelIndex&) const override;
virtual QVariant data(const QModelIndex&, int) const override;
private:
using internalData = QFileInfo;
QDir root;
QVector<internalData*> internals;
};

View File

@ -3,6 +3,9 @@
#include <QMainWindow>
#include <QString>
#include <QStringRef>
namespace Ui {
class ResourceExplorer;
}
@ -15,6 +18,37 @@ public:
~ResourceExplorer();
private:
struct MemoryMap {
const unsigned char* begin;
const unsigned char* end;
QString path;
};
enum ResourceFlags {
NoFlags = 0,
Compressed = 1,
Directory = 2
};
struct ResourceNode {
QString name;
ResourceFlags flags;
uint32_t size;
const unsigned char* start;
uint16_t country;
uint16_t language;
uint64_t lastModTimeMS;
uint32_t hash;
std::vector<ResourceNode*> children;
};
using data = const unsigned char*;
void rebuild();
std::vector<MemoryMap> readMemoryMaps();
std::vector<ResourceExplorer::ResourceNode*> parseResource(int version, data library, data names, data data );
Ui::ResourceExplorer *ui;
};

View File

@ -30,12 +30,11 @@ INCLUDEPATH += $$PWD/include/
SOURCES += \
src/injector.cpp \
src/qtdebugger.cpp \
src/qtresourcemodel.cpp \
src/resourceexplorer.cpp
HEADERS += \
include/injector.cpp \
include/qtdebugger.h \
include/qtresourcemodel.h \
include/resourceexplorer.h
FORMS += \

View File

@ -10,8 +10,10 @@
using registerFunc = bool (*) (int, const unsigned char*, const unsigned char*, const unsigned char*);
RegisteredResource::RegisteredResource(int i, const unsigned char* resourceStruct, const unsigned char* resourceName, const unsigned char* resourceData) :
i( i ), resourceStruct( resourceStruct ), resourceName( resourceName ), resourceData( resourceData ) { }
std::map<uint64_t, std::shared_ptr<RegisteredResource>>* registeredResources = nullptr;
RegisteredResource::RegisteredResource(int version, const unsigned char* resourceStruct, const unsigned char* resourceName, const unsigned char* resourceData) :
version( version ), resourceStruct( resourceStruct ), resourceName( resourceName ), resourceData( resourceData ) { }
int QApplication::exec() {
// init debugger

View File

@ -1,97 +0,0 @@
#include "qtresourcemodel.h"
#include <QFileInfo>
QtResourceModel::QtResourceModel( const QDir& root, QObject* parent ) :
QAbstractItemModel( parent ),
root( root.absolutePath() )
{
}
QtResourceModel::~QtResourceModel() {
for( internalData* inter : internals ) {
delete inter;
}
internals.clear();
}
QModelIndex QtResourceModel::index(int row, int column, const QModelIndex& parent) const {
if (!hasIndex(row, column, parent))
return QModelIndex();
internalData* parentItem;
if (parent.isValid()) {
parentItem = static_cast<internalData*>(parent.internalPointer());
} else {
parentItem = new internalData( root, "." );
// internals.append( parentItem );
}
if (parentItem->isDir()) {
QDir parentDir( parentItem->absolutePath() );
QFileInfoList entrys = parentDir.entryInfoList(QDir::Filter::AllEntries, QDir::SortFlag::DirsFirst | QDir::SortFlag::Name );
if ( entrys.size() < row ) {
internalData* childItem = new internalData( entrys.at(row) );
// internals.append( childItem );
return createIndex(row, column, childItem);
}
}
return QModelIndex();
}
QModelIndex QtResourceModel::parent(const QModelIndex& index) const {
if (!index.isValid())
return QModelIndex();
internalData* childItem = static_cast<internalData*>(index.internalPointer());
internalData* parentItem = new internalData( childItem->dir().absolutePath() );
// internals.append( parentItem );
if (parentItem->absolutePath() == root.absolutePath())
return QModelIndex();
return createIndex(0, 0, parentItem);
}
int QtResourceModel::rowCount(const QModelIndex& parent) const {
if (parent.column() > 0)
return 0;
QDir parentDir;
if( parent.isValid() ) {
internalData* parentItem = static_cast<internalData*>(parent.internalPointer());
if ( ! parentItem->isDir()) {
return 0;
}
parentDir = QDir( parentItem->absolutePath() );
} else {
parentDir = root;
}
QFileInfoList entrys = parentDir.entryInfoList(QDir::Filter::AllEntries, QDir::SortFlag::DirsFirst | QDir::SortFlag::Name );
return entrys.size();
}
int QtResourceModel::columnCount(const QModelIndex& parent) const {
return 2;
}
QVariant QtResourceModel::data(const QModelIndex& index, int role) const {
if (!index.isValid())
return QVariant();
internalData* item = static_cast<internalData*>( index.internalPointer() );
if (role == Qt::DisplayRole) {
if ( index.column() == 0)
return QVariant( item->fileName() );
if ( index.column() == 1)
return QVariant( item->size() );
}
return QVariant();
}

View File

@ -1,15 +1,207 @@
#include "resourceexplorer.h"
#include "ui_resourceexplorer.h"
#include "qtresourcemodel.h"
#include <QFile>
#include <QFileInfo>
#include <QDebug>
ResourceExplorer::ResourceExplorer(QWidget *parent) : QMainWindow( parent ), ui(new Ui::ResourceExplorer) {
#include <endian.h>
#include <list>
#include "injector.h"
#pragma pack (push, 1)
struct ResourceLibraryEntry {
uint32_t nameOffset;
uint16_t flags; // use ResourceFlags
union Content {
struct Directory {
uint32_t childCount;
uint32_t childOffset;
} directory;
struct File {
uint16_t country;
uint16_t language;
uint32_t dataOffset;
} file;
} content;
};
struct ResourceLibraryEntryV2 : public ResourceLibraryEntry {
uint64_t lastModTimeMS;
};
struct ResouceNameEntry {
uint16_t length;
uint32_t hash;
// string content
char str[0];
};
struct ResouceDataEntry {
uint32_t length;
char data[0];
};
#pragma pack (pop)
ResourceExplorer::ResourceExplorer( QWidget* parent ) : QMainWindow( parent ), ui( new Ui::ResourceExplorer ) {
this->ui->setupUi(this);
QtResourceModel* model = new QtResourceModel( QDir( ":/" ), this );
this->ui->treeView->setModel( model );
this->rebuild();
}
ResourceExplorer::~ResourceExplorer() {
delete this->ui;
}
void ResourceExplorer::rebuild() {
this->ui->treeWidget->clear();
if ( !registeredResources || registeredResources->empty() ) {
QTreeWidgetItem* resItem = new QTreeWidgetItem();
resItem->setText( 0, "No Resources Captured" );
this->ui->treeWidget->addTopLevelItem( resItem );
return;
}
std::vector<MemoryMap> memMaps = readMemoryMaps();
for ( auto it : *registeredResources ) {
// find correct memory map
auto memMapIt = std::find_if(memMaps.cbegin(), memMaps.cend(), [c = it.second](const MemoryMap& mm) { return mm.begin < c->resourceStruct && mm.end > c->resourceStruct; } );
QString fileName( "<file unknown>" );
QString filePath( "" );
if ( memMapIt != memMaps.cend() ) {
fileName = QFileInfo( memMapIt->path ).fileName();
filePath = memMapIt->path;
}
QTreeWidgetItem* resItem = new QTreeWidgetItem( );
resItem->setText( 0, fileName );
resItem->setToolTip( 0, filePath );
std::shared_ptr<RegisteredResource> r = it.second;
std::vector<ResourceNode*> resources = parseResource(r->version, r->resourceStruct, r->resourceName, r->resourceData);
this->ui->treeWidget->addTopLevelItem( resItem );
}
}
std::vector<ResourceExplorer::MemoryMap> ResourceExplorer::readMemoryMaps() {
QFile maps("/proc/self/maps");
if ( !maps.open( QIODevice::ReadOnly | QIODevice::Text ) ) {
qWarning() << "failed to read MemoryMaps";
return {};
}
// QTextStream stream(&maps);
std::vector<MemoryMap> out;
out.reserve( maps.size() / 100 ); // approximation of row count
do {
QString line = QString::fromUtf8( maps.readLine() );
if( line.isEmpty() ) continue;
QVector<QStringRef> parts = line.splitRef(' ', QString::SplitBehavior::SkipEmptyParts );
if ( parts.size() != 6) {
continue;
}
QVector<QStringRef> borders = parts.at(0).split('-');
if ( borders.size() != 2) continue;
bool ok = true;
const unsigned char* begin = (const unsigned char*) borders.at( 0 ).toULongLong( &ok, 16);
if ( !ok ) continue;
const unsigned char* end = (const unsigned char*) borders.at( 1 ).toULongLong( &ok, 16);
if ( !ok ) continue;
if ( begin > end ) std::swap( begin, end );
out.push_back( MemoryMap{ begin, end, parts.at( 5 ).toString().simplified() } );
} while( !maps.atEnd() );
return out;
}
static QString readUTF16String(const char16_t* ptr, uint32_t size) {
char16_t* newptr = new char16_t[size];
for ( uint32_t i = 0; i < size; ++i ) {
newptr[i] = be16toh(ptr[i]);
}
QString out = QString::fromUtf16(newptr, size);
delete[] newptr;
return out;
}
std::vector<ResourceExplorer::ResourceNode*> ResourceExplorer::parseResource(int version, ResourceExplorer::data libraryIndex, ResourceExplorer::data names, ResourceExplorer::data data ) {
std::vector<ResourceNode*> out;
if ( version > 2 ) {
qWarning() << "resource has unknown version: " << version;
return out;
}
const uint32_t libraryOffset = ( version == 1 ) ? sizeof(ResourceLibraryEntry) : sizeof(ResourceLibraryEntryV2);
using task_t = std::pair<uint32_t, std::vector<ResourceNode*>*>;
std::list<task_t> pendingOffsets;
pendingOffsets.push_back( {0, &out} );
while ( pendingOffsets.size() > 0) {
task_t task = pendingOffsets.front();
uint32_t offset = task.first;
pendingOffsets.pop_front();
const ResourceLibraryEntryV2* entry = (const ResourceLibraryEntryV2*) &libraryIndex[ offset * libraryOffset ];
ResourceNode* newNode = new ResourceNode();
newNode->flags = (ResourceFlags) be16toh( entry->flags );
if( offset == 0) {
newNode->name = ":";
newNode->hash = 0;
} else {
uint32_t nameOffset = be32toh(entry->nameOffset);
uint16_t nameLen = be16toh(*(uint16_t*) (names + nameOffset));
newNode->name = readUTF16String((const char16_t*) (names + nameOffset + 6), nameLen);
newNode->hash = *((uint16_t*) ( names + nameOffset + 2));
}
if ( newNode->flags & ResourceFlags::Directory ) {
// directory
newNode->size = 0;
newNode->country = 0;
newNode->language = 0;
uint32_t childCount = be32toh( entry->content.directory.childCount );
uint32_t childOffset = be32toh( entry->content.directory.childOffset );
for ( uint32_t i = 0; i < childCount; ++i ) {
pendingOffsets.push_back( { i + childOffset, &(newNode->children) } );
}
} else {
// file
newNode->country = be16toh(entry->content.file.country);
newNode->language = be16toh(entry->content.file.language);
uint32_t dataOffset = be32toh(entry->content.file.dataOffset);
const unsigned char* dataBegin = data + dataOffset;
newNode->size = *((const uint32_t*) dataBegin);
newNode->start = dataBegin + 4;
}
if ( version >= 2 ) {
newNode->lastModTimeMS = entry->lastModTimeMS;
}
task.second->push_back( newNode );
}
return out;
}

View File

@ -16,7 +16,27 @@
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTreeView" name="treeView"/>
<widget class="QTreeWidget" name="treeWidget">
<property name="columnCount">
<number>2</number>
</property>
<attribute name="headerMinimumSectionSize">
<number>50</number>
</attribute>
<attribute name="headerDefaultSectionSize">
<number>200</number>
</attribute>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">2</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>