DEV Community

Cover image for Fake C++ Compiler with Node.js - Part 1
Gurigraphics
Gurigraphics

Posted on • Edited on

Fake C++ Compiler with Node.js - Part 1

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

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

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

1.4) Download 1 favicon.ico

https://favicon.io/emoji-favicons/

1.5) Create data.txt

<a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5> 
Enter fullscreen mode Exit fullscreen mode

1.6) Generate resources.res

windres.exe -i "resources.rc" --input-format=rc -o "resources.res" -O coff 
Enter fullscreen mode Exit fullscreen mode

1.7) Compile

g++ main.cpp -o main.exe -I./ -static-libgcc -static-libstdc++ resources.res 
Enter fullscreen mode Exit fullscreen mode

1.8) Test

main 
Enter fullscreen mode Exit fullscreen mode

Console Output

<a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5> 
Enter fullscreen mode Exit fullscreen mode

1.9) Search hex

Open main.exe in an editor hexadecimal or run

certutil -encodehex main.exe hex.txt 
Enter fullscreen mode Exit fullscreen mode

1.10) Get address

Search by

<a1 
Enter fullscreen mode Exit fullscreen mode

The position of "<" I found in the column 9 of line 000a8db0
This is the beginning of the resources region

0xa8db8 
Enter fullscreen mode Exit fullscreen mode

Bytes positions

Search by

5> 
Enter fullscreen mode Exit fullscreen mode

The position of ">" I found in the column 16 of line 000a8dc8
This is the end of the resources region

0xa8dd7 
Enter fullscreen mode Exit fullscreen mode

For now just save these addresses

Part 2 - Node.js

1. Node start

mkdir node_cpp cd node_cpp mkdir uploads npm init -y 
Enter fullscreen mode Exit fullscreen mode

2. Copy "main.exe" to folder "node_cpp\uploads"

copy ..\main.exe .\uploads 
Enter fullscreen mode Exit fullscreen mode

3. Install

npm i fs npm i path 
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

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

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

9. Compile

node main.js 
Enter fullscreen mode Exit fullscreen mode

10. Run

final 
Enter fullscreen mode Exit fullscreen mode

Console Output

1234567890 
Enter fullscreen mode Exit fullscreen mode

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)