Skip to content

Commit 5f2e963

Browse files
committed
Update
making changes including a main function and using arguments
1 parent 367a29f commit 5f2e963

File tree

9 files changed

+2672
-56
lines changed

9 files changed

+2672
-56
lines changed

PyInstArchive.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ struct CTOCEntry {
2929
const std::string& getName() const {
3030
return name;
3131
}
32+
33+
bool isCompressed() const {
34+
return cmprsFlag != 0;
35+
}
3236
};
3337

3438
// Class for handling the PyInstaller Archive
@@ -43,7 +47,9 @@ class PyInstArchive {
4347
bool checkFile();
4448
bool getCArchiveInfo();
4549
void parseTOC();
46-
void viewFiles();
50+
void extractFiles(const std::string& outputDir);
51+
void displayInfo();
52+
void decompressData(const std::vector<char>& compressedData, std::vector<char>& decompressedData);
4753

4854
private:
4955
std::string filePath; // Path to the archive file

PyInstaller-C++.vcxproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
<CharacterSet>Unicode</CharacterSet>
4242
</PropertyGroup>
4343
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
44-
<ConfigurationType>StaticLibrary</ConfigurationType>
44+
<ConfigurationType>Application</ConfigurationType>
4545
<UseDebugLibraries>true</UseDebugLibraries>
4646
<PlatformToolset>v143</PlatformToolset>
4747
<CharacterSet>Unicode</CharacterSet>
@@ -117,11 +117,13 @@
117117
<PrecompiledHeader>NotUsing</PrecompiledHeader>
118118
<PrecompiledHeaderFile>
119119
</PrecompiledHeaderFile>
120+
<LanguageStandard>stdcpp17</LanguageStandard>
120121
</ClCompile>
121122
<Link>
122123
<SubSystem>
123124
</SubSystem>
124125
<GenerateDebugInformation>true</GenerateDebugInformation>
126+
<AdditionalDependencies>libz-static.lib;%(AdditionalDependencies)</AdditionalDependencies>
125127
</Link>
126128
</ItemDefinitionGroup>
127129
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -145,7 +147,6 @@
145147
</ItemDefinitionGroup>
146148
<ItemGroup>
147149
<ClInclude Include="framework.h" />
148-
<ClInclude Include="pch.h" />
149150
<ClInclude Include="PyInstArchive.h" />
150151
</ItemGroup>
151152
<ItemGroup>

Pyinstaller.cpp

Lines changed: 178 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <winsock2.h>
88
#include <random>
99
#include <sstream>
10+
#include <filesystem>
11+
#include "zlib.h"
1012

1113
#pragma comment(lib, "ws2_32.lib")
1214
#pragma comment(lib, "oleaut32.lib")
@@ -295,10 +297,182 @@ void PyInstArchive::parseTOC() {
295297
* This method iterates over the Table of Contents (TOC) and prints the names
296298
* and uncompressed sizes of the embedded files.
297299
*/
298-
void PyInstArchive::viewFiles() {
299-
std::cout << "[+] Viewing files in the archive..." << std::endl;
300+
void PyInstArchive::displayInfo() {
301+
std::cout << "[+] Archive Info:" << std::endl;
302+
// Print out relevant information about the PyInstaller archive
303+
// For example: number of files, archive version, etc.
300304
for (const auto& entry : tocList) {
301-
std::cout << entry.name << " (" << entry.uncmprsdDataSize << " bytes)" << std::endl;
305+
std::cout << "File: " << entry.getName() << ", Size: " << entry.getCompressedDataSize() << " bytes" << std::endl;
302306
}
303-
std::cout << "[+] Finished viewing files." << std::endl;
307+
}
308+
309+
/**
310+
* @brief Extracts files from a PyInstaller archive to a specified directory.
311+
*
312+
* This method iterates through the Table of Contents (TOC) of the archive and extracts each file.
313+
* For compressed entries, it decompresses the data using zlib and writes it to the output directory.
314+
* Uncompressed data is written as-is.
315+
*
316+
* @param outputDir The directory where extracted files will be saved.
317+
*
318+
* @note The output directory and its subdirectories will be created if they do not exist.
319+
* @note Errors are logged if any file extraction or decompression fails.
320+
*/
321+
void PyInstArchive::extractFiles(const std::string& outputDir) {
322+
for (const auto& tocEntry : tocList) {
323+
// Move the file pointer to the position of the compressed data
324+
fPtr.seekg(tocEntry.position, std::ios::beg);
325+
326+
// Read the compressed data into a vector
327+
std::vector<char> compressedData(tocEntry.getCompressedDataSize());
328+
fPtr.read(compressedData.data(), tocEntry.getCompressedDataSize());
329+
330+
std::vector<char> decompressedData;
331+
332+
if (tocEntry.isCompressed()) {
333+
// If the entry is compressed, decompress the data using zlib
334+
decompressedData.resize(tocEntry.uncmprsdDataSize);
335+
336+
z_stream strm = {};
337+
strm.avail_in = tocEntry.getCompressedDataSize();
338+
strm.next_in = reinterpret_cast<Bytef*>(compressedData.data());
339+
strm.avail_out = tocEntry.uncmprsdDataSize;
340+
strm.next_out = reinterpret_cast<Bytef*>(decompressedData.data());
341+
342+
if (inflateInit(&strm) != Z_OK) {
343+
std::cerr << "[!] Error: Could not initialize zlib for decompression" << std::endl;
344+
continue;
345+
}
346+
347+
int result = inflate(&strm, Z_FINISH);
348+
inflateEnd(&strm);
349+
350+
if (result != Z_STREAM_END) {
351+
std::cerr << "[!] Error: Decompression failed for " << tocEntry.getName() << std::endl;
352+
continue;
353+
}
354+
}
355+
else {
356+
// If the data is not compressed, just use the original data
357+
decompressedData = compressedData;
358+
}
359+
360+
// Construct the full output file path
361+
std::filesystem::path outputFilePath = std::filesystem::path(outputDir) / tocEntry.getName();
362+
363+
// Ensure the directory exists
364+
std::filesystem::create_directories(outputFilePath.parent_path());
365+
366+
// Write the decompressed data to the output file
367+
std::ofstream outFile(outputFilePath, std::ios::binary);
368+
if (!outFile.is_open()) {
369+
std::cerr << "[!] Error: Could not open output file " << outputFilePath << std::endl;
370+
continue;
371+
}
372+
373+
outFile.write(decompressedData.data(), decompressedData.size());
374+
outFile.close();
375+
376+
std::cout << "[+] Extracted: " << tocEntry.getName() << " (" << decompressedData.size() << " bytes)" << std::endl;
377+
}
378+
}
379+
380+
/**
381+
* @brief Decompresses data using zlib.
382+
*
383+
* Decompresses `compressedData` into `decompressedData` using zlib.
384+
* Ensure `decompressedData` has enough space for the decompressed output.
385+
*
386+
* @param compressedData Input vector of compressed data.
387+
* @param decompressedData Output vector for decompressed data.
388+
*
389+
* @note Prints an error message if decompression fails.
390+
*/
391+
void PyInstArchive::decompressData(const std::vector<char>& compressedData, std::vector<char>& decompressedData) {
392+
uLongf decompressedSize = decompressedData.size();
393+
int result = uncompress(reinterpret_cast<Bytef*>(decompressedData.data()), &decompressedSize,
394+
reinterpret_cast<const Bytef*>(compressedData.data()), compressedData.size());
395+
396+
if (result != Z_OK) {
397+
std::cerr << "[!] Error: Decompression failed" << std::endl;
398+
// Optionally, you could also throw an exception or handle the error more specifically
399+
}
400+
}
401+
402+
/**
403+
* @brief Parses command-line arguments for interacting with a PyInstaller archive.
404+
*
405+
* This method processes the command-line arguments, checks if the required parameters
406+
* are provided, and then opens the specified PyInstaller archive. It can either display
407+
* information about the archive or extract its files to the specified output directory.
408+
*
409+
* @param argc The number of command-line arguments.
410+
* @param argv The array of command-line arguments.
411+
*
412+
* @note The command must be either "-i" to display archive information or "-u" to extract files.
413+
* The archive path is required, and an optional output directory can be specified.
414+
* @note If the output directory does not exist, it will be created automatically.
415+
* @note Errors are logged if any arguments are invalid or if the archive cannot be processed.
416+
*/
417+
void parseArgs(int argc, char* argv[]) {
418+
if (argc < 3) {
419+
std::cerr << "[!] Usage: " << argv[0] << " [-i | -u] <archive_path> [output_dir]" << std::endl;
420+
exit(1);
421+
}
422+
423+
std::string command = argv[1]; // Command (-i or -u)
424+
std::string archivePath = argv[2]; // Archive file path
425+
std::string outputDir = (argc > 3) ? argv[3] : "unpacked"; // Output directory (default to "output")
426+
427+
// Check if the output directory exists, create it if it doesn't
428+
if (!std::filesystem::exists(outputDir)) {
429+
std::filesystem::create_directories(outputDir);
430+
}
431+
432+
PyInstArchive archive(archivePath);
433+
434+
if (!archive.open()) {
435+
std::cerr << "[!] Error: Could not open " << archivePath << std::endl;
436+
return;
437+
}
438+
439+
if (!archive.checkFile()) {
440+
std::cerr << "[!] Error: Invalid file " << archivePath << std::endl;
441+
return;
442+
}
443+
444+
if (!archive.getCArchiveInfo()) {
445+
std::cerr << "[!] Error: Could not extract TOC from " << archivePath << std::endl;
446+
return;
447+
}
448+
449+
if (command == "-i") {
450+
archive.displayInfo(); // Display information about the archive (filenames, sizes)
451+
}
452+
else if (command == "-u") {
453+
archive.extractFiles(outputDir); // Extract files to the specified directory
454+
}
455+
else {
456+
std::cerr << "[!] Unknown command: " << command << std::endl;
457+
}
458+
}
459+
460+
/**
461+
* @brief The entry point for the application.
462+
*
463+
* This function processes the command-line arguments by calling `parseArgs`,
464+
* which handles the input commands to either display information or extract
465+
* files from a PyInstaller archive.
466+
*
467+
* @param argc The number of command-line arguments.
468+
* @param argv The array of command-line arguments.
469+
*
470+
* @return Returns 0 upon successful execution.
471+
*
472+
* @note The `parseArgs` function handles any errors or invalid arguments,
473+
* so no error handling is required in this function.
474+
*/
475+
int main(int argc, char* argv[]) {
476+
parseArgs(argc, argv);
477+
return 0;
304478
}

README.md

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ A C++ tool to inspect and extract contents from PyInstaller archives.
1717
### Build
1818
1. Clone the repo:
1919
```sh
20-
git clone https://github.com/your_username/PyInstallerArchiveViewer.git
21-
cd PyInstallerArchiveViewer
20+
git clone https://github.com/pyinstxtractor/Pyextract.git
21+
cd Pyextract
2222
```
2323
2. Build:
2424
3. ```
@@ -27,35 +27,7 @@ A C++ tool to inspect and extract contents from PyInstaller archives.
2727

2828
### Run
2929
sh
30-
PyInstallerArchiveViewer.exe path/to/your/archive
30+
PyInstaller-C++.exe path/to/your/executable
3131

3232

33-
## Example
3433

35-
```cpp
36-
#include <iostream>
37-
#include "PyInstArchive.h"
38-
39-
int main(int argc, char* argv[]) {
40-
if (argc != 2) {
41-
std::cerr << "Usage: " << argv[0] << " <path/to/pyinstaller_archive>" << std::endl;
42-
return 1;
43-
}
44-
45-
PyInstArchive archive(argv[1]);
46-
47-
if (!archive.open()) return 1;
48-
if (!archive.checkFile()) {
49-
archive.close();
50-
return 1;
51-
}
52-
if (!archive.getCArchiveInfo()) {
53-
archive.close();
54-
return 1;
55-
}
56-
57-
archive.viewFiles();
58-
archive.close();
59-
60-
return 0;
61-
}

libz-static.lib

791 KB
Binary file not shown.

pch.cpp

Lines changed: 0 additions & 5 deletions
This file was deleted.

pch.h

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)