cft

Hacking Python Applications: Methods & Pynject Payloads

An overview of some of the methods we can use to reverse a python application for which we don't have the source. Introduction to some of pynject's bundled payloads.


user

Alec Cureau

a year ago | 4 min read

Reversing Methods

In the last post, I talked about the development of pynject and the research leading up to it. Our target is a pygame game that's bundled with pyinstaller. When we left off we had just achieved code injection. Now the question remains, what exactly comes next? The demonstration showed me modifying a variable in a program I wrote. How do we hack this game knowing nothing about how it's written? Well, there are a slue of techniques we can use to learn a bit more information about what's going on behind the scenes.

Global Symbols

One of the most powerful tools in our arsenal is python's 'globals()' function. This will return a dictionary containing the symbols in the global scope as keys, and the objects they are mapped to as values. Consider the following code.

def someFunc(): localVar = 22something = "nothing"

If we were to get the globals from this script, our variable something would be returned. But our function's local variable would not. The only way we can get variables from the local scope would be to use python's 'locals()' function from within the target function. Nevertheless, we can get some quite powerful information from the globals alone. Global variables are often used as flags, so pay special attention to integers and booleans.

Garbage Collection & Objects

Given that python's garbage collector is enabled (it is by default), we can get a list of all objects currently being tracked. We can do this by importing the 'gc' module, and using the 'gc.get_objects()' function.

The Disassembler

The disassembler is an essential tool for reversing functions, though it can be used on many more instance types. To disassemble a function we need to import the 'dis' module and use the 'dis.dis()' function. An example of the output is as follows.

def printMe(thing): print(thing)===========Turns into===========2 0 LOAD_GLOBAL 0 (print) 2 LOAD_FAST 0 (thing) 4 CALL_FUNCTION 1 6 POP_TOP 8 LOAD_CONST 0 (None) 10 RETURN_VALUE

You can find a full list of disassembly instructions and their meanings here.

Enumerating Instance Children

We have a few options when it comes to enumerating children of an instance. If we want to get the attributes of a class we can use 'dir()', 'vars()', or 'gc.get_referents()'. The 'dir' and 'vars' functions do not require any imports. 'dir' will return a dictionary containing attributes of our class and it's base classes. 'vars' will only return a dictionary containing attributes of the specified class.

'gc.get_referents' will return a list containing every object referred to by the object, which includes it's attributes and methods.

And so on...

There are many, many more methods you could use to get more information about your target. We haven't even touched on pdb. Nonetheless, if you use each of the methods above in combination you begin to get a very clear idea of the data available to manipulate, and a basic understanding of the flow of some functions and methods.

Pynject Payloads

Here is where the bundled payloads come in. What if I told you that I've written payloads which make it effortless to employ all of the above tactics? Well... I have. They come bundled with pynject.

The Executor

This is a script I wrote to make it easy to manage your scripts, and execute them without the need to call functions of the C API. Your scripts will be executed in a local scope. This means if you need to modify a global variable, you will need to declare it in the scope first as follows.

myGlobal = "This didn't work..."global myGlobalmyGlobal = "Now it works!"

The executor redirects console output to it's own console window to make it easier to debug scripts. The executor also has a number of API functions of it's own. They allow for things like module injection into applications with limited paths (i.e. pyinstaller), filtering it's own objects from the globals, and finding object instances by type. As an added bonus, the executor cleans up after itself very well. It also spawns in its own thread.

The Inspector

This is a script which automates the inspection of objects in a target process. Every method I mentioned above is done in the background, and the results inserted into trees. There's not much to say about it beyond what you can see for yourself. It's still very early in development like most of this project, and I am a single developer. There are a few glitches I'm aware of but it will get the job done.

Conclusion

So, we're armed with a working knowledge of reverse engineering python processes. Now what? Well, now we hack Rift Wizard! Next time anyway.

Upvote


user
Created by

Alec Cureau

I am a software developer currently in college. My strongest language is python. I am interested in pretty much all things development, however I am particularly keen on reverse engineering lately.


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles