Embedding Big Files in an Executable - Windows - Part 4
Gurigraphics
Posted on March 19, 2023
A file with this size 16515603 in hexadecimal is 0x1302FC. To store large values, up to 4 gigabytes, we will need 4 bytes:
0xFC, 0x02, 0x13, 0x00
We will use:
bytFile.insert(bytFile.end(), {
static_cast<char>((bytText.size() >> 24) & 0xFF),
static_cast<char>((bytText.size() >> 16) & 0xFF),
static_cast<char>((bytText.size() >> 8) & 0xFF),
static_cast<char>((bytText.size() >> 0) & 0xFF)
});
1) Let's update the patch:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
std::vector<char> file_read_bin(const std::string& fileName) {
std::string filePath = fileName;
std::ifstream file(filePath, std::ios::binary);
if (file.fail()) {
std::cerr << "Erro ao abrir o arquivo " << filePath << std::endl;
}
// Read bytes
std::vector<char> bytFile((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
return bytFile;
}
void file_write_bin(const std::string& filePath, std::vector<char> bytFile) {
// Write bytes
std::ofstream outFile(filePath, std::ios::binary);
if (outFile.fail()) {
std::cerr << "Erro ao abrir o arquivo " << filePath << " para escrita" << std::endl;
}
outFile.write(bytFile.data(), bytFile.size());
outFile.close();
std::cout << "Arquivo alterado com sucesso" << std::endl;
}
int main(int argc, char *argv[]){
if (argc != 4) {
std::cout << "Require 3 args: program.exe data.txt 163857\n";
return 1;
}
const char* program_file = argv[1];
const char* data_file = argv[2];
std::vector<char> bytFile = file_read_bin( program_file );
std::vector<char> bytText = file_read_bin( data_file );
int correctSize = std::stoi(argv[3]);
int size = bytFile.size();
int diff = size - correctSize;
// Logs
std::cout << "size: " << size << "\n";
std::cout << "correctSize: " << correctSize << "\n";
// Remove old code
if(size > correctSize ){
bytFile.erase(bytFile.end() - diff, bytFile.end() );
}
// Add content
bytFile.insert(bytFile.end(), bytText.begin(), bytText.end());
// Add content size
bytFile.insert(bytFile.end(), {
static_cast<char>((bytText.size() >> 24) & 0xFF),
static_cast<char>((bytText.size() >> 16) & 0xFF),
static_cast<char>((bytText.size() >> 8) & 0xFF),
static_cast<char>((bytText.size() >> 0) & 0xFF)
});
// Add file name
std::string data_filename_str(data_file);
data_filename_str+="\0";
bytFile.insert(bytFile.end(), data_filename_str.c_str(), data_filename_str.c_str() + data_filename_str.size() );
// Add name size
bytFile.insert( bytFile.end(), data_filename_str.size() );
// Add symbol exist new file
std::string symbol = "^";
bytFile.insert(bytFile.end(), symbol.c_str(), symbol.c_str() + symbol.size() );
// Rewrite
file_write_bin(program_file, bytFile);
return 0;
}
2) Compile
g++ patch.cpp -o patch
3) Update the program.cpp
As the function is getting too big with too many responsibilities, now let's refactor the code and separate these functions:
int getFileNamesize(std::vector<char> bytFile, int size)
std::string getFileName(std::vector<char> bytFile, int size, int fileNameSize)
int getContentSize(std::vector<char> bytFile, int size, int fileNameSize)
std::string getContent(std::vector<char> bytFile, int size, int fileNameSize, int contentSize, int contentStart)
int getContent(std::vector<char> bytFile, int size)
program.cpp
#include <fstream>
#include <iostream>
#include <windows.h>
#include <sstream>
#include <string>
#include <locale>
int assetsCount = 0;
std::vector<char> file_read_bin(const std::string& fileName) {
std::string filePath = fileName;
std::ifstream file(filePath, std::ios::binary);
if (file.fail()) {
std::cerr << "Erro ao abrir o arquivo " << filePath << std::endl;
}
// Read bytes
std::vector<char> bytFile((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
return bytFile;
}
std::string getHexValue(const std::vector<char>& bytFile, int size, int byteCount) {
std::stringstream ss;
ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(static_cast<unsigned char>(bytFile[size - byteCount]));
return ss.str();
}
int hexToInt(std::string hexStr) {
return std::stoi(hexStr, nullptr, 16);
}
int getFileNamesize(std::vector<char> bytFile, int size){
std::string sizeHex = getHexValue(bytFile, size, 2);
return hexToInt( sizeHex );
}
std::string getFileName(std::vector<char> bytFile, int size, int fileNameSize){
std::string fileName = "";
int startfileName = fileNameSize + 2;
for (int i = startfileName; i > 2; i--) {
std::string byteInStringFormat;
byteInStringFormat.push_back(static_cast<char>(bytFile[size - i])); // convert byte to char
fileName += byteInStringFormat;
}
return fileName;
}
int getContentSize(std::vector<char> bytFile, int size, int fileNameSize){
std::string byte_24 = getHexValue(bytFile, size, fileNameSize + 3);
std::string byte_16 = getHexValue(bytFile, size, fileNameSize + 4);
std::string byte_08 = getHexValue(bytFile, size, fileNameSize + 5);
std::string byte_00 = getHexValue(bytFile, size, fileNameSize + 6);
std::stringstream ss;
ss << std::hex << byte_00 << byte_08 << byte_16 << byte_24;
int contentSize;
ss >> contentSize;
return contentSize;
}
std::string getContent(std::vector<char> bytFile, int size, int fileNameSize, int contentSize, int contentStart){
std::string content = "";
for (int i = contentStart; i > fileNameSize+3+3; i--) {
std::string byteInStringFormat;
byteInStringFormat.push_back(static_cast<char>(bytFile[size - i])); // convert byte to char
content += byteInStringFormat;
}
return content;
}
int getContent(std::vector<char> bytFile, int size) {
if( bytFile[size - 1] != '^') { // Exist new file?
std::cout << "end" << "\n";
return 0;
}
assetsCount+=1;
std::cout << assetsCount << "-----------------" << "\n";
// Get file name size in last byte
int fileNameSize = getFileNamesize(bytFile, size);
std::cout << "content name size: " << fileNameSize << "\n";
// Get file name in last byte
std::string fileName = getFileName(bytFile, size, fileNameSize);
std::cout << "content name: " << fileName << "\n";
// Get content size in 4 bytes
int contentSize = getContentSize(bytFile, size, fileNameSize);
std::cout << "content size: " << contentSize << "\n";
// Get content
int contentStart = contentSize+fileNameSize+3+3;
std::string content = getContent(bytFile, size, fileNameSize, contentSize, contentStart);
// Print big string
int stringSize = content.size();
int maxLineSize = 80;
for (int i = 0; i < stringSize; i += maxLineSize) {
int lineSize = std::min(stringSize - i, maxLineSize);
std::cout << content.substr(i, lineSize) << std::endl;
}
// End
int final = size - (contentStart + 1);
std::cout << "-----------------" << "\n";
getContent(bytFile, final+1); // get new file
return 0;
}
int main(int argc, char *argv[]){
// Read file
std::string filename = std::string(argv[0]) + ".exe";
std::vector<char> bytFile = file_read_bin( filename );
int size = bytFile.size();
// Logs
std::cout << "file size: " << size << std::endl;
std::cout << "last byte: " << getHexValue(bytFile, size, 1) << std::endl;
//std::cout << "int size : " << contentSize << std::endl;
// console "pt-BR" accentuation errors
system("chcp 1252 > nul");
setlocale(LC_ALL, "pt_BR.UTF-8");
getContent(bytFile, size);
return 0;
}
4) Compile
g++ program.cpp -o program
5) Exec and get "file size"
program
file size: 172032
last byte: 00
end
6) Create um big content data3.txt
Create the file "data3.txt" and add this content:
https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js
To make it easier to verify that everything went right, you can add a "ã" at the beginning and end of this file:
ã/*! jQuery v3.6.4 |
(C.jQuery=C.$=S),S});ã
7) Apply pacth
patch program.exe data3.txt 172032
8) Run program.exe
program
file size: 261846
last byte: 5e
1-----------------
content name size: 9
content name: data3.txt
content size: 89799
ã/*! jQuery v3.6.4
(...)
ned"==typeof e&&(C.jQuery=C.$=S),S});ã
-----------------
end
8) Apply a new patch with new size
patch program.exe data3.txt 261846
9) Run program.exe
First clear console
cls
program
Result
file size: 351660
last byte: 5e
1-----------------
content name size: 9
content name: data3.txt
content size: 89799
ã/*! jQuery v3.6.4 |
(...)
ned"==typeof e&&(C.jQuery=C.$=S),S});ã
-----------------
2-----------------
content name size: 9
content name: data3.txt
content size: 89799
ã/*! jQuery v3.6.4 |
(...)
ned"==typeof e&&(C.jQuery=C.$=S),S});ã
-----------------
end
Embedding Files in an Executable - SDL/Windows - Part 5
https://dev.to/gurigraphics/embedding-files-in-an-executable-sdlwindows-part-5-2he4
Posted on March 19, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.