DEV Community

Cover image for Embedding Big Files in an Executable - Windows - Part 4
Gurigraphics
Gurigraphics

Posted on

Embedding Big Files in an Executable - Windows - Part 4

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

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

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

2) Compile

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

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

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;
}

Enter fullscreen mode Exit fullscreen mode

4) Compile

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

5) Exec and get "file size"

program
Enter fullscreen mode Exit fullscreen mode
file size: 172032
last byte: 00
end
Enter fullscreen mode Exit fullscreen mode

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 |
Enter fullscreen mode Exit fullscreen mode
(C.jQuery=C.$=S),S});ã
Enter fullscreen mode Exit fullscreen mode

7) Apply pacth

patch program.exe data3.txt 172032
Enter fullscreen mode Exit fullscreen mode

8) Run program.exe

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

8) Apply a new patch with new size

patch program.exe data3.txt 261846
Enter fullscreen mode Exit fullscreen mode

9) Run program.exe

First clear console

cls
Enter fullscreen mode Exit fullscreen mode
program
Enter fullscreen mode Exit fullscreen mode

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

Embedding Files in an Executable - SDL/Windows - Part 5

https://dev.to/gurigraphics/embedding-files-in-an-executable-sdlwindows-part-5-2he4

Top comments (0)