Unpacking PyArmor with CPython


Some people think that PyArmor is the obfuscator gold standard for python, but you can easly mess with CPython API, here is what we gonna see it this little article.


The Genesis - Injecting Python Code


The fondation of unpacking PyArmor is injecting python code, he can be easly done with CPython API combined with DLL Injection, for starting you will create a new dll project in visual studio.



Once you have your project ready it's time to import some CPython libs, simply go in Properties,VC++ Directory,Include Directory add CPython Include, and in Lib Directory add CPython libs.




If you successfully included CPython into your project you should have no problem including Python.h.


So there here is a simple code for testing CPython Injection, we will use PyRun_SimpleString function.
DWORD WINAPI MainThread(HMODULE hModule)
{
    Py_SetProgramName(L"pyinject");
    PyEval_InitThreads(); // in newest version of python is not really necessary
    PyGILState_STATE s = PyGILState_Ensure(); /* Ensure that the current thread is ready to call the Python
C API, regardless of the current state of Python, or of its
thread lock.*/
    PyRun_SimpleString("print('Injected !')");
    PyGILState_Release(s);
    CloseHandle(hModule);
    FreeLibraryAndExitThread(hModule, 0);
    return 0;
}
			



Now you can hit compile and test this out, i have created a little script wich print "Waiting for inject..." everytime i hit any key
while True:
	print("Waiting for inject...")
	input()


So if we inject the dll into python.exe we should get "Injected !" message




Now that we know the CPython api is working let's unpack pyarmor !


The Actual unpacking part


When you can inject python code into any python script it become really easy to unpack or crack any type of python program, i have created a simple python script wich looks like that
def auth():
	return False

while True:
	input("user> ")
	if(auth()):
		print("Welcome Admin !")
	else:
		print("You are not authorized.")
				
The goal is to unpack the auth function this can be easly do with dis lib.


Here is the result


As you can see this is a huge mess just for a return False function, why ?


This is the downside of this method, pyarmor encrypt all functions,bytecodes,etc.. and only decrypt them at runtime when the function is used.


But ! You can still replace the function or call it, and that's pretty interesting if your plan is to crack a python program.


End


As you can see is not an easy task to unpack/crack a python script protected with PyArmor but with some works you can get it !