📜 ⬆️ ⬇️

Add multiplayer to the game using Node.JS and Frida. Part 1



On Habré, there are already a couple of mentions about the Frida tool ( "Frida-node or a little bit of strange code" , "Javascript of JavaScript and Reverse Engineering" ). One article already mentions the use of Frida in practice, however, almost everywhere the tool is used as a framework for reverse engineering and the study of program functionality (maybe even hacking).

I want to talk about the process of turning one of my favorite single-player toys into a full-fledged, multiplayer.

I just want to warn you: in such a process, I am almost a novice, so I would not be surprised if rotten tomatoes fly from the system programming gurus to me. On the other hand, I hope that my article will allow other beginners to start using Frida (and not only), and I will get something useful for myself from the angry comments of the guru. Also continue to write articles (with positive ratings of course), I will be right in the process of developing multiplayer.
')
Given:
Node.Js + Frida + frida-node
Street Legal Racing: Redline
SLRR: java pack

Install Frida


Much time has passed since the release of the first version of frida-node. The module got its own Frida binaries, so the installation can be simply painted in steps:
  1. Download and install Node.Js (at the time of writing of article 5.4.1)
  2. When installing, do not forget to poke the npm installation checkbox
  3. Create the directory with the desired name (for the project) in the right place for us, start the console in it and enter npm install frida-node
  4. I hope the installation went well.

Whats with the game




A little game story.

The game itself was released in 2003 by Activision. The game was developed by the Hungarian company Invictus Games. It happened that the game, because of poor marketing, because of the apparent inconsistency with the interests of the audience, did not take off. Despite this, the game has several communities of fans, most likely due to the peculiarities
Gameplay: The car can almost completely disassemble and assemble, change and adjust the details, very realistic (at that time) physics, body deformation, and behavior on the road. Several times the community tried to get the source codes of the game by signing petitions and sending them to Invictus. The developers refused to transfer them, citing problems with rights and the company Activision, which currently owns the rights. Despite this, it somehow happened that part of the java code of the game had flowed into the network. Some old and stripped-down version of the JVM (Java Virtual Machine, even without throw-catch support) is built into the game itself, the functions for working with the network are completely absent, and files are saved and opened only in the built-in game format.

At the moment, our countryman, with the nickname RAXAT, was released unofficial patch 2.3.0LE, which became
standard for this game. On the basis of this patch, I decided to add multiplayer support to the game.

Unsuccessful and successful attempts





Just want to note that I am not the first from the community who wanted to implement multiplayer in the game. I also made several attempts to add the necessary functions to the game. Initially, I did this using a dll written in Delphi, which was injected into the process. This method worked, but adding something to the module was quite a laborious task.

The Frida tool came to the rescue, which allows the V8 engine to be introduced into the JavaScript process, and to work with the “from the inside” process. The most important task at first was to add data exchange between the JVM of the game and the external Node.js process.

The project code is on Github: https://github.com/lailune/SLRRMultiplayer, however, it is a purely test version, and what I have at the moment.

I don’t provide a link to the game for obvious reasons.

The script loaded inside the process will be called injectScript.js. The name of the application script itself is not important, I called it app.js.

Our script will run the game's binary, and transfer the pid of the process to the Frida module to implement the script.

var frida = require('frida'); var spawn = require('child_process').spawn;  var injectScript = fs.readFileSync('injectScript.js', "utf8"); var workingDir = 'C:/SLRR/'; //        //       process.chdir(workingDir); var gameProcess = spawn(            workingDir + 'StreetLegal_Redline.exe',            [], {                        stdio: 'inherit'            });  // id  « » AttachHook(gameProcess.pid); 


The AttachHook function itself contains all the initialization code for Frida:
  1. Connect to the process
  2. We load our internal script into the embedded V8 (at this time, the V8 checks the script for errors and compiles it into byte code)
  3. Put the message handler from the script
  4. Run the script, and display a message about success or error.


 function AttachHook(pid) {           frida.attach(pid)                       .then(function (session) {                                  return session.createScript(injectScript);                       })                       .then(function (script) {                                  script.events.listen('message',function (message, data) {                                              handleMessage(script, message.payload.name, message.payload.data);                                  });                                  script.load()                                              .then(function () {                                                          console.log('Hook script injected.');                                              })                                              .catch(function(error) {                                                          console.log('Hook Error:', error.message);                                              });                       }) } 


In the case of the arrival of a “payload” from our embedded script, we must process it in our own way. To do this, there is the handleMessage function, in which, however, only the reception of information about the player’s position in virtual space is realized so far.

 //     var pos = {x: 0.0, y: 0.0, z: 0.0, sy: 0.0, sp: 0.0, sr: 0.0, angle: 0.0}; function handleMessage(script, type, data) {           if (type == "POS") {                       var tmp = data.split(';');                       pos.x=tmp[0];                       pos.y=tmp[1];                       pos.z=tmp[2];           } } 


Now we can dispose of this data as we like, for example: transfer them to the server.

injectScript.js


The method of transferring data from the game I chose is extremely outrageous: I intercept the call to CreateFileA
Why:
  1. This is the easiest way. Just open the file with
    “Necessary name”, inside which will be the data that we transmit.
  2. I could not teach Frida to look for the right one.
    text in the memory of the application, for further use of a certain
    memory areas.
  3. It works.

An important point: in this script, you must carefully monitor the creation of variables and memory allocation. If too many variables are created, then at one point, the garbage collector will start, and the process will freeze for a while. Also, if you constantly allocate memory, you get a leak, which even the GC can not cope.

 //    (  ) var dummy = Memory.allocAnsiString("\\\\nothing\\dev\\null"); var message=""; //    CreateFileA.  Hook Interceptor.attach(Module.findExportByName('kernel32.dll','CreateFileA'), {           onEnter: function onEnter(args) {                       //                          message = Memory.readUtf8String(args[0]);                       //    DTM^ (  )                       //      DTM^payloadname^data                       if (message.indexOf('DTM^') != -1) {                  //,  ,  «»                      message = message.split('^');                                  send({name: message[1], data:message[2]});                    }           },           onLeave: function onLeave(retval) {           } }); 


Perhaps at this stage, many will have comments that I will be glad to hear.

Some java


Since the game uses a stripped-down version of Java inside itself, you have to write a little in this great language.

An interesting point: the java compiler in the byte code for the JVM is built right inside the game, just put the java file in the appropriate directory inside the src folder, and when you start the game, a class file will be created.

For the test, I used the City class (it implements the basic functions for managing the city in the game). In the future, I plan to bring the implementation of my pseudo-socket into a separate global class.

While realizes only data transmission outside.

 public class MultiplayerSocket {           int connected = 0; //            File dtm;           public void MultiplayerSocket(){                       dtm = new File();           }           public int send(String type, String msg) {                       // «»    ,                          dtm = new File("&nofolder\\DTM^" + type + "^" + msg);                       dtm.open(File.MODE_READ); //    (         error.log)                       dtm.close(); //. -                           return 0; //                 } } //   MultiplayerSocket MP;          ….           public void enter(GameState prev_state ){                                  //                                  MP = new MultiplayerSocket();                                  ….          }          ….           //                 public void sendPositionDatagram() {                       if (MP) {                                  if (player.car && player.car.chassis) {                                              Vector3 pos = player.car.getPos(); //                                                MP.send("POS", pos.x + ";" + pos.y + ";" + pos.z); //                                   }                       }           }           //     ( 30 fps)           public void frame(){                       sendPositionDatagram();           } 


What's next?


We must learn to transfer data inside the game. Finding the right line in the memory with Frida has not yet succeeded (a la ArtMoney, hands are golden, but they do not grow from there). It will be great if there are people who tell me how. There was an implementation with data exchange via a file (and the game has its own format), it was slow, unstable, lagging. In thoughts, try to send data through the substitution of the return of some function (but ideally you need through memory). Also in terms of integration into the game itself, there is a lot of work. The content of the following articles depends on the progress of the project.

Source: https://habr.com/ru/post/275235/


All Articles