EINFÜHRUNG IN PE-DATEIRESOURCEN
Grüße, unsere lieben Leser!
In diesem Artikel werden wir Ihnen einen wichtigen und interessanten Teil der PE-Dateien vorstellen: PE IMAGE RESOURCES (Daten, die nach der Kompilierung mit dem Programm verknüpft sind). Wir werden in die internen Strukturen des Ressourcenbaums eintauchen und unseren eigenen nativen Ressourcen-Parser schreiben, ohne WinAPI zu verwenden.
Ressourcen in PE (Portable Executable)-Dateien sind eingebettete Daten, die einen integralen Bestandteil der Anwendung bilden. Sie umfassen eine Vielzahl von Elementen wie Bilder, Symbole, Textzeichenfolgen, Dialogfelder, Schriftarten. Diese Ressourcen sind direkt in die ausführbare Datei integriert, was ihre Verfügbarkeit für die Anwendung während der Ausführung sicherstellt. Es ist wichtig zu beachten, dass Ressourcen wie Bilder, Texte und Töne oft erst nach der Kompilierung des Programms hinzugefügt werden. Dieser Ansatz bietet mehrere bedeutende Vorteile und macht den Entwicklungsprozess flexibler und effizienter.
Stellen Sie sich vor, Sie sind Teil eines Entwicklungsteams, das daran arbeitet, eine neue Anwendung zu erstellen. Ihr Team besteht aus Programmierern, Designern und Inhaltsverwaltern. Jeder von Ihnen trägt dazu bei, etwas Einzigartiges und Nützliches zu schaffen.
Zu Beginn des Projekts konzentrieren sich die Programmierer darauf, Code zu schreiben und zu testen. Sie erstellen das Framework der Anwendung und stellen sicher, dass alle Funktionen korrekt funktionieren. Gleichzeitig arbeiten Designer und Inhaltsmanager daran, Ressourcen zu erstellen - Bilder, Klänge, Texte für die Schnittstelle. Diese parallele Arbeit ermöglicht es dem Team, schnell und effizient voranzukommen.
Wenn der Hauptteil der Programmierung abgeschlossen ist, ist es Zeit, die Ressourcen in die Anwendung zu integrieren. Dies wird mit speziellen Werkzeugen durchgeführt, die es ermöglichen, Ressourcen zu einer bereits kompilierten Anwendung hinzuzufügen, ohne dass eine Neukompilierung erforderlich ist. Das ist sehr praktisch, insbesondere wenn Änderungen vorgenommen oder Ressourcen aktualisiert werden müssen - es ist nicht notwendig, das gesamte Projekt neu zu kompilieren.
Einer der Schlüsselaspekte dieses Prozesses ist die Lokalisierung. Dank der Trennung der Ressourcen vom Hauptcode wird das Lokalisieren der Anwendung wesentlich einfacher. Schnittstellentexte, Fehlermeldungen können leicht übersetzt und ersetzt werden, ohne den Hauptcode zu beeinträchtigen. Dies ermöglicht es, die Anwendung für verschiedene Sprachmärkte anzupassen, sodass sie für eine breite Nutzerbasis weltweit zugänglich und verständlich wird.
BEISPIELE FÜR RESSOURCENNUTZUNG
- Anwendungssymbole: Symbole, die im Windows Explorer oder in der Taskleiste angezeigt werden, bieten eine visuelle Darstellung der Anwendung.
- Dialogfenster: Definitionen von Dialogfenstern, die die Anwendung zur Benutzerinteraktion anzeigt, wie Einstellungen oder Warnfenster.
- Menüs: Menüstrukturen, die in der Benutzeroberfläche verwendet werden, bieten Navigation und Funktionalität.
- Zeichenketten: Lokalisierte Zeichenketten, die verwendet werden, um Text in der Anwendung anzuzeigen, einschließlich Fehlermeldungen, Tooltips und anderen Benutzeroberflächen.
- Klänge: Audiodateien, die von der Anwendung in bestimmten Situationen abgespielt werden können, wie z. B. Klangbenachrichtigungen.
- Cursors: Grafische Cursor, die zur Interaktion mit der Benutzeroberfläche verwendet werden, wie Pfeile, Zeiger oder animierte Cursor.
- Versionsinformationen (Version Info): Enthält Informationen über die Anwendungsversion, das Urheberrecht, den Produktnamen und andere versionsbezogene Daten.
- Manifest: Eine XML-Datei, die Informationen über die Anwendungskonfiguration enthält, einschließlich Anforderungen an die Windows-Version und Sicherheitseinstellungen.
- RC_DATA: Vom Entwickler definierte beliebige Daten, die binäre Daten, Konfigurationsdateien oder andere anwendungsspezifische Ressourcen enthalten können.
WIE KANN MAN RESSOURCEN IN EINER PORTABLEN EXECUTABLE-DATEI ANSEHEN UND BEARBEITEN?
Können wir Ressourcen in einer kompilierten Anwendung anzeigen und bearbeiten? Absolut! Alles, was Sie benötigen, ist das richtige Werkzeug. Es gibt Werkzeuge, die eine breite Palette von Möglichkeiten bieten, um mit Ressourcen in kompilierten ausführbaren Dateien zu arbeiten, einschließlich Anzeigen, Bearbeiten, Hinzufügen und Löschen von Ressourcen.
Hier ist eine Liste von Ressourcen-Editoren, die verwendet werden können, um Ressourcen in bereits kompilierten ausführbaren Dateien anzusehen oder zu bearbeiten:
- Resource Hacker: Dies ist ein Ressourcen-Editor für 32-Bit- und 64-Bit-Windows-Anwendungen. Er ermöglicht es Ihnen, Ressourcen in ausführbaren Dateien (.exe, .dll, .scr usw.) und kompilierten Ressourcenbibliotheken (.res, .mui) anzusehen und zu bearbeiten. Resource Hacker
- ResEdit: Ein kostenloser Ressourcen-Editor für Win32-Programme. Geeignet für die Arbeit mit Dialogen, Symbolen, Versionsinformationen und anderen Arten von Ressourcen. ResEdit
- Resource Tuner: Ein Ressourcen-Editor, der es Ihnen ermöglicht, ausführbare Dateien mit Problemen zu öffnen und versteckte Daten zu bearbeiten, die andere Editoren einfach nicht sehen. Resource Tuner
- Resource Builder: Ein leistungsfähiger, voll ausgestatteter Ressourcen-Editor für Windows. Ermöglicht es Ihnen, Ressourcendateien zu erstellen, zu bearbeiten und zu kompilieren (.RC, .RES und andere), sowie Ressourcen in kompilierten ausführbaren Dateien zu bearbeiten. Resource Builder
- Visual Studio: Bietet einen Ressourcen-Editor, der es Ihnen ermöglicht, Ressourcen hinzuzufügen, zu löschen und zu modifizieren. Visual Studio
- Resource Tuner Console: Ein leistungsfähiges Befehlszeilen-Werkzeug zur Bearbeitung von Ressourcen, ideal für den Einsatz in Batch-Dateien (.bat). Resource Tuner Console
- Qt Centre: Ermöglicht Ihnen, Ressourcendateien von kompilierten ausführbaren Dateien mit Qt zu bearbeiten. Qt Centre
Lassen wir uns den ersten Werkzeug auf der Liste genauer ansehen: Resource Hacker.
Dieses Tool ermöglicht es Ihnen nicht nur, Ressourcen aus einer ausführbaren Datei anzusehen und zu extrahieren, sondern auch sie zu bearbeiten!
Diese Ressourcen, zu denen Symbole, Menüs, Dialogfelder und andere Datentypen gehören können, befinden sich typischerweise in einem speziellen Abschnitt der PE-Datei, der als .rsrc (Ressourcenabschnitt) bekannt ist. Es ist jedoch wichtig zu beachten, dass dies keine strikte Regel ist und Ausnahmen auftreten können.
Ein wesentlicher Aspekt beim Navigieren und Zugreifen auf diese Ressourcen innerhalb einer PE-Datei ist das IMAGE_DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_RESOURCES]. Dieser Verzeichniseintrag ist Teil des optionalen Headers der PE-Datei, speziell innerhalb des Arrays der Datendirektorien. Er dient als Zeiger oder Referenz für die Ressourcen im Bild. Das IMAGE_DIRECTORY_ENTRY_RESOURCES bietet Informationen über den Standort (wie die relative virtuelle Adresse) und die Größe der Ressourcendaten.
RESSOURCENSTRUKTUR IN PORTABLEN AUSFÜHRBAREN DATEIEN
Allgemeiner Überblick
Lassen Sie uns einen detaillierten Blick auf die Strukturen werfen, die im Ressourcenabschnitt einer PE (Portable Executable)-Datei verwendet werden. Der Ressourcenabschnitt in Windows PE-Dateien hat eine einzigartige dreistufige hierarchische Baumstruktur. Dieser Baum wird verwendet, um Ressourcen wie Symbole, Cursor, Zeichenketten, Dialoge und andere zu organisieren und darauf zuzugreifen. So ist er strukturiert:
Stufe 1: Ressourcentypen
An der obersten Ebene des Baumes befinden sich die Ressourcentypen. Jeder Ressourcentyp kann entweder durch einen numerischen Kennzeichner (ID) oder durch einen String-Namen identifiziert werden.
Stufe 2: Ressourcennamen
Auf der zweiten Ebene hat jeder Ressourcentyp seine eigenen Namen oder Kennungen. Dies ermöglicht es Ihnen, mehrere Ressourcen desselben Typs zu haben, wie beispielsweise mehrere Symbole oder Zeilen.
Stufe 3: Ressourcensprachen
Auf der dritten Ebene hat jede Ressource Varianten für verschiedene Sprachlokalisierungen. Dies ermöglicht es, dieselbe Ressource, wie zum Beispiel einen Dialog, in verschiedenen Sprachen zu lokalisieren.
Datenstrukturen
Die folgenden Datenstrukturen werden verwendet, um diese Hierarchie darzustellen:
- IMAGE_RESOURCE_DIRECTORY: Diese Struktur stellt einen Kopf für jede Ebene des Baumes dar und enthält allgemeine Informationen über die Einträge auf dieser Ebene.
- IMAGE_RESOURCE_DIRECTORY_ENTRY: Dies sind Elemente, die entweder Unterverzeichnisse sein können (die auf ein anderes IMAGE_RESOURCE_DIRECTORY zeigen) oder die endgültigen Blätter des Baumes, die auf die tatsächlichen Ressourcendaten zeigen.
- IMAGE_RESOURCE_DATA_ENTRY: Diese Struktur weist auf die Ressourcendaten selbst hin und enthält deren Größe und Offset.
Die Visualisierung des Ressourcenbaums könnte folgendermaßen aussehen:
Root (IMAGE_DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_RESOURCES].VirtualAddress)
|
+-- Type (RT_ICON, RT_STRING, ...)
|
+-- Name (ID or String)
|
+-- Language (Locale ID)
|
+-- Data (Actual resource data)
IMAGE_RESOURCE_DIRECTORY
|
|-- IMAGE_RESOURCE_DIRECTORY_ENTRY (Resource Types)
| |-- IMAGE_RESOURCE_DIRECTORY (Resource names)
| | |-- IMAGE_RESOURCE_DIRECTORY_ENTRY (Names)
| | | |-- IMAGE_RESOURCE_DIRECTORY (Languages)
| | | | |-- IMAGE_RESOURCE_DIRECTORY_ENTRY (Languages)
| | | | | |-- IMAGE_RESOURCE_DATA_ENTRY (Resource data)
Jeder Knoten in diesem Baum repräsentiert ein IMAGE_RESOURCE_DIRECTORY, und die Blätter sind IMAGE_RESOURCE_DATA_ENTRIES, die direkt auf die Ressourcendaten verweisen. Wenn ein Entwickler Ressourcen manuell parst, muss er von der Wurzel ausgehend durch diesen Baum gehen und alle Ebenen sequenziell navigieren, um die notwendigen Daten zu finden.
IMAGE_RESOURCE_DIRECTORY
Diese Struktur dient als Kopfzeile für jede Ebene des Ressourcenbaums und enthält Informationen über die Einträge auf dieser Ebene.
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries;
WORD NumberOfIdEntries;
// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
- Characteristics: Typischerweise ungenutzt und auf 0 gesetzt.
- TimeDateStamp: Der Zeitstempel der Ressourcenerstellung.
- MajorVersion und MinorVersion: Die Version des Ressourcenverzeichnisses.
- NumberOfNamedEntries: Die Anzahl der Ressourceneinträge mit Namen.
- NumberOfIdEntries: Die Anzahl der Ressourceneinträge mit numerischen Kennungen.
IMAGE_RESOURCE_DIRECTORY_ENTRY
Elemente, die entweder Unterverzeichnisse oder die letzten Blätter des Baumes sein können.
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union {
struct {
DWORD NameOffset:31;
DWORD NameIsString:1;
};
DWORD Name;
WORD Id;
};
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
};
};
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
- Name: Wenn NameIsString auf 1 gesetzt ist, enthält dieses Feld einen Offset, der auf einen UNICODE-String zeigt, der den Namen der Ressource darstellt. Wenn NameIsString auf 0 gesetzt ist, wird das Id-Feld verwendet, um die Ressource über eine numerische Kennung zu identifizieren.
- OffsetToData: Wenn DataIsDirectory auf 1 gesetzt ist, enthält dieses Feld einen Offset, der auf ein weiteres IMAGE_RESOURCE_DIRECTORY (d.h. ein Unterverzeichnis) zeigt. Wenn DataIsDirectory auf 0 gesetzt ist, zeigt dieser Offset auf einen IMAGE_RESOURCE_DATA_ENTRY.
IMAGE_RESOURCE_DATA_ENTRY
Diese Struktur verweist auf die tatsächlichen Daten der Ressource.
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData;
DWORD Size;
DWORD CodePage;
DWORD Reserved;
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
- OffsetToData: Der Offset vom Anfang des Ressourcenabschnitts zu den Ressourcendaten.
- Size: Die Größe der Ressourcendaten in Bytes.
- CodePage: Die für die Kodierung der Ressourcendaten verwendete Codepage.
- Reserved: Reserviert; typischerweise auf 0 gesetzt.
WICHTIG! Offsets in IMAGE_RESOURCE_DIRECTORY und IMAGE_RESOURCE_DIRECTORY_ENTRY werden vom Start der Ressourcen (IMAGE_DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_RESOURCES].VirtualAddress) berechnet, und nur die Offsets in IMAGE_RESOURCE_DATA_ENTRY werden vom Start des Basis-Images berechnet!
LASS UNS EINEN NATIVEN RESSOURCEN-PARSER SCHREIBEN!
Es ist Zeit, unseren eigenen nativen Ressourcen-Parser zu schreiben, ohne WinAPI zu verwenden! Das manuelle Parsen von Ressourcen, anstatt Windows API-Funktionen wie EnumResourceTypes oder EnumResourceNames zu verwenden, hat mehrere Vorteile, insbesondere im Kontext der Sicherheitsanalyse und des Antivirus-Scannens:
- Sicherheit: API-Funktionen wie EnumResourceTypes und EnumResourceNames erfordern, dass die ausführbare Datei in den Adressraum des Prozesses geladen wird, was zur Ausführung von bösartigem Code führen kann, wenn die Datei Viren oder Trojaner enthält. Die manuelle Analyse von Ressourcen vermeidet dieses Risiko.
- Plattformunabhängigkeit: Die manuelle Ressourcenanalyse ist nicht abhängig von der Betriebssystemversion und ihrer WinAPI, was sie zu einer universelleren Lösung macht.
- Heuristische Analyse: Die manuelle Analyse ermöglicht die Anwendung komplexer Heuristiken und Erkennungsalgorithmen, die notwendig sein können, um neue oder unbekannte Bedrohungen zu identifizieren.
- Leistung: Das Parsen kann für eine bessere Leistung im Vergleich zur Verwendung der WinAPI optimiert werden, besonders beim Scannen einer großen Anzahl von Dateien.
- Kontrolle: Mit der manuellen Analyse hat der Analyst die volle Kontrolle über den Prozess und kann ihn für spezifische Analysebedürfnisse feinabstimmen, während API-Funktionen begrenzte Kontrolle bieten und möglicherweise nicht alle Aspekte der Ressourcen aufzeigen.
- Schutz: Malware kann verschiedene Methoden verwenden, um die Erkennung zu vermeiden, einschließlich der Manipulation von Ressourcen auf eine Weise, dass sie von Standard-APIs nicht erkannt werden. Die manuelle Analyse ermöglicht die Erkennung solcher Manipulationen.
- Vollzugriff: API-Funktionen bieten möglicherweise keinen Zugang zu allen Ressourcen, besonders wenn diese beschädigt oder absichtlich verändert wurden. Die manuelle Analyse ermöglicht die Analyse aller Daten ohne die Einschränkungen, die durch die API auferlegt werden.
- Fehlerbehandlung: Bei der Verwendung von API-Funktionen kann die Fehlerbehandlung begrenzt sein, während die manuelle Analyse flexiblere Reaktionen auf nicht standardmäßige Situationen und Anomalien in der Dateistruktur ermöglicht.
struct ResourceInfo
{
DWORD Size; // Size of the resource data
PBYTE data; // Offset of the resource data from the beginning of the file
union {
WORD TypeID; // Resource type ID or
PIMAGE_RESOURCE_DIR_STRING_U Type; // resource type
};
union {
WORD NameID; // Resource name ID or
PIMAGE_RESOURCE_DIR_STRING_U Name; // resource name
};
WORD Language; // Language of the resource
};
std::optional> getAllResources(BYTE* pBase, uint64_t fileSize)
{
IMAGE_RESOURCE_DIRECTORY* pTypesDirectory = nullptr;
std::vector resources;
try
{
//********************************************************
// parse PE header
//********************************************************
IMAGE_DOS_HEADER* pDosHeader = reinterpret_cast(pBase);
IMAGE_NT_HEADERS* pNtHeaders = reinterpret_cast(pBase + pDosHeader->e_lfanew);
// Verify that the PE signature is valid, indicating a valid PE file.
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
return std::nullopt;
// Depending on the machine type (32-bit or 64-bit), obtain the resource directory data.
IMAGE_DATA_DIRECTORY resourceDirectory;
switch (pNtHeaders->FileHeader.Machine)
{
case IMAGE_FILE_MACHINE_I386:
resourceDirectory = reinterpret_cast(pNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
break;
case IMAGE_FILE_MACHINE_AMD64:
resourceDirectory = reinterpret_cast(pNtHeaders)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
break;
default:
return std::nullopt;
};
// If the resource directory is empty, exit as there are no resources.
if (resourceDirectory.Size == 0)
return std::nullopt;
// Convert the RVA of the resources to a RAW offset
uint64_t resourceBase = ntpe::RvaToOffset(pBase, resourceDirectory.VirtualAddress);
IMAGE_RESOURCE_DIRECTORY* pResourceDir = reinterpret_cast(pBase + resourceBase);
//********************************************************
// Start parsing the resource directory
//********************************************************
// Iterate through type entries in the resource directory.
// parse types
pTypesDirectory = pResourceDir;
IMAGE_RESOURCE_DIRECTORY_ENTRY* pTypeEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pTypesDirectory + 1);
for (uint64_t ti = 0; ti < pTypesDirectory->NumberOfNamedEntries + pTypesDirectory->NumberOfIdEntries; ti++)
{
// parse names
IMAGE_RESOURCE_DIRECTORY_ENTRY* pTypeEntry = &pTypeEntries[ti];
IMAGE_RESOURCE_DIRECTORY* pNamesDirectory = (IMAGE_RESOURCE_DIRECTORY*)(pBase + (pTypeEntry->OffsetToDirectory & 0x7FFFFFFF) + resourceBase);
for (uint64_t ni = 0; ni < pNamesDirectory->NumberOfNamedEntries + pNamesDirectory->NumberOfIdEntries; ni++)
{
// parse langs
IMAGE_RESOURCE_DIRECTORY_ENTRY* pNamesEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pNamesDirectory + 1);
IMAGE_RESOURCE_DIRECTORY_ENTRY* pNameEntry = &pNamesEntries[ni];
IMAGE_RESOURCE_DIRECTORY* pLangsDirectory = (IMAGE_RESOURCE_DIRECTORY*)(pBase + (pNameEntry->OffsetToDirectory & 0x7FFFFFFF) + resourceBase);
for (uint64_t li = 0; li < pLangsDirectory->NumberOfNamedEntries + pLangsDirectory->NumberOfIdEntries; li++)
{
// parse data
IMAGE_RESOURCE_DIRECTORY_ENTRY* pLangsEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pLangsDirectory + 1);
IMAGE_RESOURCE_DIRECTORY_ENTRY* pLangEntry = &pLangsEntries[li];
IMAGE_RESOURCE_DATA_ENTRY* pDataEntry = (IMAGE_RESOURCE_DATA_ENTRY*)(pBase + resourceBase + pLangEntry->OffsetToData);
// Save the resource information in a structured format.
ResourceInfo entry = {};
entry.Language = pLangsEntries->Id;
entry.Size = pDataEntry->Size;
entry.Type = (PIMAGE_RESOURCE_DIR_STRING_U)(pTypeEntry->NameIsString) ? (PIMAGE_RESOURCE_DIR_STRING_U)(pBase + pTypeEntry->NameOffset + resourceBase) : (PIMAGE_RESOURCE_DIR_STRING_U)(pTypeEntry->Id);
entry.Name = (PIMAGE_RESOURCE_DIR_STRING_U)(pNameEntry->NameIsString) ? (PIMAGE_RESOURCE_DIR_STRING_U)(pBase + pNameEntry->NameOffset + resourceBase) : (PIMAGE_RESOURCE_DIR_STRING_U)(pNameEntry->Id);
entry.data = ntpe::RvaToRaw(pBase, pDataEntry->OffsetToData);
resources.push_back(entry);
}
}
}
return resources;
}
catch (std::exception&)
{
return std::nullopt;
};
}
Sie können den Code des gesamten Projekts auf unserem GitHub finden:
https://github.com/SToFU-Systems/DSAVE
Der Ressourcen-Parser gibt einen Vektor mit Strukturen zurück, die Zeiger auf Ressourcentypen, deren Namen und den Sprachidentifikator jeder Ressource enthalten. In jeder Struktur befindet sich ein Zeiger auf die Ressourcendaten. Jede Struktur bleibt so lange gültig, wie die ausführbare Datei, die wir gelesen und geparst haben, im Speicher ist. Dies ist sehr praktisch für das Schreiben eigener Antivirenprogramme und das Scannen von Dateien. Nachdem die Datei freigegeben wurde, werden die Zeiger ungültig.
LISTE DER VERWENDETEN WERKZEUGE
- PE Tools: https://github.com/petoolse/petools Dies ist ein Open-Source-Tool zur Manipulation von PE-Headerfeldern. Unterstützt x86- und x64-Dateien.
- Resource Hacker: https://www.angusj.com/resourcehacker. Dies ist ein Ressourcen-Editor für 32-Bit- und 64-Bit-Windows-Anwendungen. Es ermöglicht Ihnen, Ressourcen in ausführbaren Dateien (.exe, .dll, .scr, etc.) und kompilierten Ressourcenbibliotheken (.res, .mui) anzusehen und zu bearbeiten.
SCHLUSSFOLGERUNG
Und das war's, Freunde!
Wir haben die Ressourcen von PORTABLE_EXECUTABLE Dateien des Windows-Betriebssystems erforscht und unseren eigenen, einfachen, aber recht effektiven nativen Ressourcenparser geschrieben!
Wir schätzen Ihre Unterstützung und freuen uns auf Ihre weiterhin aktive Teilnahme in unserer Gemeinschaft!
Alle Fragen an die Autoren des Artikels können per E-Mail gesendet werden: articles@stofu.io
Danke!