Injecting Arbitrary Python Into EVE Online

Sep 23, 2009 19:08

(Some quick notes before the article. My Pokerstars reversing series
is still in the works, but I've been on vacation for a few weeks and
haven't given it the attention it deserves. There'll be a new post
shortly. Also, I'm currently looking for contract work, and you can
see some info here: http://daeken.com/open-for-contracts )
I finally broke down and installed EVE Online and registered for a
trial, since quite a few of my friends have been talking about it
nonstop. Being the dork I am, I looked through the binaries that were
installed, and I immediately noticed that they shipped Stackless
Python 2.5 as a separate binary (python25.dll). Of course, this led
me to an investigation of what I could accomplish with respect to
modifying the client without actually patching any code. A few hours
later, I was able to run any Python code inside of EVE.
All of the code is available from the Github repository here:
http://github.com/daeken/EveInject
Injector

First, you have to get some sort of code into the game process. My
favorite tool for this stuff is EasyHook, as it lets you
inject .NET code into processes.
EasyHook, as the name implies, is very easy. You don't even need to
hook anything here, just get some code running in the process. With a
simple boilerplate NAnt build file and the source of an earlier
hooking project, it only takes a few minutes to strip it down to what
we need. EveInjector.cs finds the 'exefile' process for EVE and reads
the given Python file into a string. It then uses RemoteHooking to
inject EIInject.dll into the process.
In EIInject.boo, you can see what exactly is going on. In the Run
method, the following are called: Py_Initialize, PyRun_SimpleString,
and RemoteHooking.WakeUpProcess. The Initialize call should've
already happened, but this prevents the client from crashing if the
injection happens very early in the load process. The PyRun call runs
the code you gave, and the WakeUpProcess call causes the EVE process
to come back to life, although it never seems to actually go to sleep.
We now have control over the EVE process.
Logging

If you notice, there's a LogServer.exe binary in the EVE directory.
This will display all sorts of interesting information from the EVE
client. We need to be able to write to this.
In my early experimentation, I noticed that traceback.print_exc()
would go to the log server, so it was clear that stderr was tied to
the log. By doing the following, you can print to the log directly:

import sys
sys.stdout = sys.stderr

Modules from compiled.code

All Python code in the client is Stackless Python bytecode and split
up into a few files: lib/corelib.ccp, lib/corestdlib.ccp,
lib/evelib.ccp, script/compiled.code. You can read about the details
of these files on the http://wiki.evemu-project.co.cc/eveFileFormats if you want
to play with them. Anyway, the compiled.code file holds the bulk of
the game code, but if you inspect sys.modules, you won't see anything
from there. That's because it's handled by the 'nasty' module in
libccp.
To expose these modules to sys.modules (and thus make it easy to import them):

import sys
import nasty
for key, value in nasty.nasty.mods.items():
sys.modules[key] = value

Services

In the 'svc' module, you can see all the service classes available on
the client, but you can't directly instantiate any of them. Instead,
you have to use the 'eve' instance. The 'eve' instance is the center
of everything, client-wise. To get to services:

from eve import eve
gameui = eve.LocalSvc('gameui')

You can see the methods available in the gameui instance via the svc module:

from svc import gameui
print dir(gameui)

Future

From here, getting to the bytecode of the functions is fairly easy, so
it'd be nice to have an in-process decompiler that dumps the code of
all modules, without having to mess around with the files directly.
There are also a large number of bugs in the current injector, which I
think stem from this code running in another thread from the normal
Python interpreter. I'm fairly sure there's some way to acquire the
GIL for the duration of the injected code running, which should fix
it.
All that said, I think there's some fun stuff that can be done with
this. From here, it wouldn't take a huge amount of work to build
something akin to Macroquest, where you can extend the UI, automate
tasks, and generally make life better inside the client. With some
tweaking, it's possible to make this fairly difficult to detect, but
I'd be careful using this on an account you really care about early
on.
Happy Hacking,
- Cody Brocious (Daeken)
Posted via email from I, Hacker
Previous post Next post
Up