Embedding Files in an Executable - Windows - Part 2
Gurigraphics
Posted on March 19, 2023
Why embed files inside an executable?
Why use a patch instead of a compiler and resources?
- To create a singleFile.exe, without folders and dependencies.
- For the common user not to have access to the files.
- To be able to use a "patch" instead of a "compiler" to create a program. And being able to do it on any operating system, without having to install anything.
- For the "build" to be instantaneous.
- For a server to be able to "compile everything", without needing hundreds gigs of memory.
This method is equivalent to using resources. The difference is that it does not require compilation to change the program content. The program can be updated on both Linux and Windows, without having to install libraries and dependencies and compilers. The program base works as a "template".
1) Create the program template
#include <fstream>
#include <iostream>
#include <windows.h>
#include <sstream>
#include <string>
#include <locale>
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 << "File open error " << 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 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();
// Get content size in last byte
std::string sizeHex = getHexValue(bytFile, size, 1);
int contentSize = hexToInt( sizeHex );
// 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");
// print content
std::string content = "content: ";
for (int i = contentSize+1; i > 1; i--) {
std::string byteInStringFormat;
byteInStringFormat.push_back(static_cast<char>(bytFile[size - i])); // convert byte to char
content += byteInStringFormat;
}
std::cout << content << std::endl;
return 0;
}
2) Compile
g++ program.cpp -o program
3) Exec and get file size
program
file size: 163840
last byte: 00
int size : 0
content:
Or get file size by cmd
for %I in (program.exe) do @echo %~zI
163840
4) Create 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(), bytText.size() );
// Rewrite
file_write_bin(program_file, bytFile);
return 0;
}
5) Create content file: data.txt
1234567890Xkãj56
6) Apply patch
The correct file size was 163840;
This is used to split the file and not add patch over patch.
patch program.exe data.txt 163840
7) Run program.exe
program
file size: 163858
last byte: 11
int size : 17
content: 1234567890Xkãj56
8) Change content file: data.txt
123
9) Apply patch
patch program.exe data.txt 163840
10) Run program.exe
program
file size: 163844
last byte: 03
int size : 3
content: 123
One way to improve this would be to use a "linked list" to store more files in sequence.
Embedding Multiples Files in an Executable - Windows - Part 3
https://dev.to/gurigraphics/embedding-multiples-files-in-an-executable-windows-part-3-4pn8
Posted on March 19, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.