Why fake?
Let's use the system Embedding Files in an Executable to turn Node.js into a Fake C++ Compiler.
Fake because it will change the content of resources directly from binary or hexadecimal.
What's the use of that?
For a server to compile a C++ program we need to install several gigabytes of libraries, a dedicated Linux or Windows server, a reasonable amount of RAM and CPU to do the processing.
To just modify an executable we will only need a server node.js.
We can even do this with javascript on the client-side frontend.
Part 1 - C++
1.1) Create main.cpp
#include <iostream>
#include "resources.h"
int main() {
std::cout << GetResource() << std::endl;
return 0;
}
1.2) Create resources.h
#include <windows.h>
#define IDI_MYICON 101
#define TEXTFILE 256
#define IDR_DATA1 255
void LoadFileInResource(int name, int type, DWORD& size, const wchar_t*& data){
HMODULE handle = ::GetModuleHandleW(NULL);
HRSRC rc = ::FindResourceW(handle, MAKEINTRESOURCEW(name), MAKEINTRESOURCEW(type));
HGLOBAL rcData = ::LoadResource(handle, rc);
size = ::SizeofResource(handle, rc);
data = static_cast<const wchar_t*>(::LockResource(rcData));
}
std::string GetResource(){
DWORD size = 0;
const char* data = NULL;
LoadFileInResource(IDR_DATA1, TEXTFILE, size, reinterpret_cast<const wchar_t*&>(data));
std::string content(data, size);
return content;
}
1.3) Create resources.rc
#include <windows.h>
#include "resources.h"
IDI_MYICON ICON "favicon.ico"
IDR_DATA1 TEXTFILE "data.txt"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "CompanyName"
VALUE "LegalCopyright", "(c) CompanyName. All Rights reserved."
VALUE "ProductName", "ProductName"
VALUE "ProductVersion", "1,0,0,1"
VALUE "FileDescription", "ProductName"
VALUE "InternalName", "ProductName"
VALUE "OriginalFilename", "main.exe"
VALUE "ShellIntegrationVersion", "2"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0416, 1200
END
END
1.4) Download 1 favicon.ico
https://favicon.io/emoji-favicons/
1.5) Create data.txt
<a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5>
1.6) Generate resources.res
windres.exe -i "resources.rc" --input-format=rc -o "resources.res" -O coff
1.7) Compile
g++ main.cpp -o main.exe -I./ -static-libgcc -static-libstdc++ resources.res
1.8) Test
main
Console Output
<a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5>
1.9) Search hex
Open main.exe in an editor hexadecimal or run
certutil -encodehex main.exe hex.txt
1.10) Get address
Search by
<a1
The position of "<" I found in the column 9 of line 000a8db0
This is the beginning of the resources region
0xa8db8
Search by
5>
The position of ">" I found in the column 16 of line 000a8dc8
This is the end of the resources region
0xa8dd7
For now just save these addresses
Part 2 - Node.js
1. Node start
mkdir node_cpp
cd node_cpp
mkdir uploads
npm init -y
2. Copy "main.exe" to folder "node_cpp\uploads"
copy ..\main.exe .\uploads
3. Install
npm i fs
npm i path
3. Create path_join.js
This file will be used to resolve the directories path
const path = require('path')
const path_join = function( file, folder ){
if( folder ) return path.join(__dirname, `/${folder}/${ file }`)
return path.join(__dirname, `/${ file }`)
}
module.exports = path_join;
4. Create slicer.js
This file will be used to cut the executable into 3 parts: header, middle, footer
Middle is the part that we want to modify and update using just node.js.
const fs = require('fs')
const slicer = function( exe_path, result_path, entry_start, entry_end ){
var file_data;
// Set positions
const file_start = parseInt( entry_start, 16 );
const file_end = parseInt( entry_end, 16 );
// Get exe
const input_data = fs.readFileSync( exe_path );
// Slice
if( entry_end == "end" ){
file_data = input_data.slice( file_start )
}else{
file_data = input_data.slice( file_start, file_end )
}
// Write
fs.writeFileSync( result_path, file_data );
console.log('Binary file sucessfull created!');
}
module.exports = slicer;
5. Create data.txt in "node_cpp/uploads"
This will be the content we will replace
Attention: this content cannot be greater than the previous content
1234567890
6. Create replaceBinaryText.js
This file will be used to take the contents of middle.exe and generate a new_middle.exe with the new contents. If the new content is smaller, it will fill with 0x0000 until it reaches the original size.
const fs = require('fs')
const replaceBinaryText = function( data_path, input_path, output_path ){
// Get bin
const exe = fs.readFileSync( input_path );
const min_size = exe.length;
// Get text
const input_data = fs.readFileSync( data_path );
const data_size = input_data.length;
if( data_size > min_size ){
console.log(`File Error. Maximum size is ${min_size}`);
}else{
// Fill
const padding_buffer = Buffer.alloc(min_size - data_size, 0x00);
// Concat
const output_buffer = Buffer.concat([input_data, padding_buffer]);
// Write
fs.writeFileSync(output_path, output_buffer, 'binary');
console.log('Binary file sucessfull created!');
}
}
module.exports = replaceBinaryText;
7. Create merge3binary.js
This file will be used to join the 3 binaries: header.exe, middle.exe and footer.exe to create the final.exe.
const fs = require('fs')
const merge3binary = function( header, middle, footer, output_path ){
const header_data = fs.readFileSync( header );
const middle_data = fs.readFileSync( middle );
const footer_data = fs.readFileSync( footer );
const merged_buffer = Buffer.concat([header_data, middle_data, footer_data]);
fs.writeFileSync(output_path, merged_buffer, 'binary');
console.log('Binary file sucessfull created!');
}
module.exports = merge3binary;
8. Create main.js
Finally let's put it all together. Now let's use those addresses 0xa8db8 and 0xa8dd7 + 1
var slicer = require('./slicer')
var path_join = require('./path_join')
var merge3binary = require('./merge3binary')
var replaceBinaryText = require('./replaceBinaryText')
const main = function(){
var folder = "uploads"
var input_path = path_join( "main.exe", folder )
var output_path = path_join( "final.exe", folder )
var header_path = path_join( "main_header.exe", folder )
var middle_path = path_join( "main_middle.exe", folder )
var footer_path = path_join( "main_footer.exe", folder )
var data_path = path_join( "data.txt", folder )
var new_path = path_join( "main_middle_updated.exe", folder )
var adress_start = "0xa8db8"
var adress_end = "0xa8dd8" // 0xa8dd7 + 1
// -------- Generate 3 slices
slicer( input_path, header_path, "0x00000", adress_start)
slicer( input_path, middle_path, adress_start, adress_end)
slicer( input_path, footer_path, adress_end, "end")
// -------- Update resources region
replaceBinaryText( data_path, middle_path, new_path )
// Merge 3 binary
merge3binary(header_path, new_path, footer_path, output_path)
}
main()
9. Compile
node main.js
10. Run
final
Console Output
1234567890
Now whenever you want to modify it, you just need to modify the content of data.txt, and run node main.js and finish.
Fake C++ Compiler with Node.js - Part 2
https://dev.to/gurigraphics/fake-c-compiler-with-nodejs-server-part-2-eag
Top comments (0)