Embedding Files in an Executable - Windows - Part 2

gurigraphics

Gurigraphics

Posted on March 19, 2023

Embedding Files in an Executable - Windows - Part 2

Why embed files inside an executable?
Why use a patch instead of a compiler and resources?

  1. To create a singleFile.exe, without folders and dependencies.
  2. For the common user not to have access to the files.
  3. 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.
  4. For the "build" to be instantaneous.
  5. 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;
}
Enter fullscreen mode Exit fullscreen mode

2) Compile

g++ program.cpp -o program
Enter fullscreen mode Exit fullscreen mode

3) Exec and get file size

program
Enter fullscreen mode Exit fullscreen mode
file size: 163840
last byte: 00
int size : 0
content:
Enter fullscreen mode Exit fullscreen mode

Or get file size by cmd

for %I in (program.exe) do @echo %~zI
163840
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

5) Create content file: data.txt

1234567890Xkãj56
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

7) Run program.exe

program
Enter fullscreen mode Exit fullscreen mode
file size: 163858
last byte: 11
int size : 17
content: 1234567890Xkãj56
Enter fullscreen mode Exit fullscreen mode

8) Change content file: data.txt

123
Enter fullscreen mode Exit fullscreen mode

9) Apply patch

patch program.exe data.txt 163840
Enter fullscreen mode Exit fullscreen mode

10) Run program.exe

program
Enter fullscreen mode Exit fullscreen mode
file size: 163844
last byte: 03
int size : 3
content: 123
Enter fullscreen mode Exit fullscreen mode

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

💖 💪 🙅 🚩
gurigraphics
Gurigraphics

Posted on March 19, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related