I, Hacker

Hungry hungry macros 
« Back to blog

Injecting Arbitrary Python Into EVE Online

(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 EVEmu Wiki 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)

Loading mentions Retweet

Comments (15)

Sep 23, 2009
Chris Allen said...
What the heck happened to Alky? Don't think I've forgotten about that.
Sep 23, 2009
Cody Brocious said...
Alky's been dead for well over two years now, as the project was killed (and open sourced) when my old company folded.
Sep 23, 2009
japherwocky said...
very cool, very nice writeup
Sep 24, 2009
ldle said...
This might of some interest to you. http://trac.cbwhiz.com/pyhack
Oct 20, 2009
Paul said...
Hey, what environment you using to build this. Bit new to c# and none of the tools I've tried seem to recognize the .build format.
Oct 20, 2009
Paul said...
Sorry, premature post. Looks like NAnt + boo tasks for others wondering.
Nov 07, 2009
tehozzeh said...
The threading issues can be fixed by using the PyGILState_Ensure(), and PyGILState_Release() functions, cleared up all the crashes I was having.
Nov 18, 2009
Levi said...
Hmm, i have syntax error on
import sys
(log server message)
Nov 24, 2009
life_dev said...
Have trouble with PyRun_SimpleString, the game just freezzes. Previosly i calling the PyGILState_Ensure and then Py_Initialize.
Nov 26, 2009
aerialdirector said...
Sort of wondering if I even compiled this correctly....after passing test.py on the command line, no changes whatsoever. LogServer.exe shows no errors or any changes. I compiled with NANT + booc - I have Visual Studio 2008 installed. Any tips? Anyways, thanks for your work on this.
Dec 06, 2009
tehozzeh said...
Using EasyHook is just one option, this does the equivalent in C.
http://pastie.org/private/75nd99r0oncv47olr2jhjw
Dec 07, 2009
Some random lurker said...
tehozzeh, when compiling that code Visual C++ 2008 gives syntax errors, like "syntax error : missing ';' before identifier 'GetDirectoryFile'" and "missing type specifier - int assumed. Note: C++ does not support default-int"

Am I doing something wrong?

Dec 07, 2009
Some random lurker said...
You know what, nvm, I fail. I do however get a
"(158) : error C3861: 'PYTHON_CALL': identifier not found"
Feb 16, 2010
InternetSpaceDude said...
Any new info on this Daeken/Tehozzeh/Anyone? Have you made anything useful with this yet? How would I go about changing the auto pilot warp distance to 0?
Mar 07, 2010
DJDD said...
Aye. Anymore progress on this? I've managed to get it all working and am currently playing around with EVEs functions, but I'd love to colaborate with anyone on this as it can be quite a slow process.

Leave a comment...

 
Got an account with one of these? Login here, or just enter your comment below.
Posterous-login    twitter