rhythm Posted June 16, 2021 Posted June 16, 2021 Somebody has any suggestion for decompiling pyArmor Obfuscated code (main.pyc)? I have not experience in python decompiling. Someone attacked our entities with this malware and I want to study the actual malicious code. You can download this malware at ... https://app.any.run/tasks/0fea95f7-25cf-4b7a-b26b-f26ac4f1995d/ Malware Source: https://www.adobe-flash-player.cc/down/flash_installer.exe main.pyc
kao Posted June 16, 2021 Posted June 16, 2021 (edited) This post explains the unpacking method quite well: Edited June 16, 2021 by kao 1 1
Extreme Coders Posted June 18, 2021 Posted June 18, 2021 Some observations about the sample: Python version used => 3.7.8 Pyarmor version => r40.14 Pyarmor mode => Super Mode The sample uses PyInstaller encryption with key "SvbQ0ZZC5HTuwipn". Pyc files inside the pyz directory can be decrypted by following the steps here (the first snippet which uses AES CFB mode) Other than main.pyc, the files of interest are (AutoRun, Browsers, ComMod, Config, FileManager, Flash, Installer, Rerun, SNS, Sandbox, Screenshot, Shell, StringEncode, WiFi, Zip) which can be found in the outpyz directory The sample doesn't use the binding feature of PyArmor, Hence the techniques linked in the above comment can be directly applied. It's possible to replace the Python37.dll with our own, compiled from source. However make sure to use Python 3.7.8 as mentioned in (1) Because of (6) you can also import the individual pyc files (5) in a Python 3.7 REPL to have a look around. However make sure to do this in a VM with network disabled. Spoiler C:\Users\Administrator\AppData\Local\Temp>python Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)] on win32 >>> import Sandbox >>> dir(Sandbox) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'check', 'pyarmor', 'wmi'] >>> import ComMod >>> dir(ComMod) ['Log', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'b264decode', 'b264encode', 'b64decode', 'b64encode', 'base64decode', 'base64encode', 'compress', 'decode', 'decompress', 'dumps', 'encode', 'get_time', 'loads', 'os', 'pyarmor', 'time'] >>> import FileManager >>> dir(FileManager) ['ComMod', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'allDisk', 'curpath', 'delete', 'download', 'dumps', 'exists', 'files', 'getsize', 'isdir', 'isfile', 'listdir', 'localtime', 'pyarmor', 'remove', 'requests', 'stat', 'strftime', 'upload'] >>> import Screenshot >>> dir(Screenshot) ['ComMod', 'CreateProcessAsUser', 'NORMAL_PRIORITY_CLASS', 'STARTUPINFO', 'Shell', 'WTSQueryUserToken', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'b64decode', 'b85decode', 'os', 'pyarmor', 'randint', 'requests', 'run_as_active_session_user', 'sc', 'sleep', 'sys', 'windll'] >>> import WiFi >>> dir(WiFi) ['Shell', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'get_nearby_wifi', 'get_saved_wifi_list', 'get_wifi_info', 'pyarmor'] >>> import Browsers >>> dir(Browsers) ['ChromeDecode', 'Cipher', 'CryptUnprotectData', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'algorithms', 'base64', 'browsers', 'copyfile', 'ctypes', 'datetime', 'default_backend', 'json', 'modes', 'os', 'pyarmor', 'sqlite3'] >>> import main >>> dir(main) ['AutoRun', 'Browsers', 'C', 'ComMod', 'Config', 'FileManager', 'Flash', 'Installer', 'Rerun', 'SNS', 'Sandbox', 'Screenshot', 'Shell', 'StringEncode', 'Thread', 'WiFi', 'Zip', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'ctypes', 'dumps', 'freeze_support', 'loads', 'os', 'protect_pytransform', 'pyarmor', 'random', 'requests', 'sleep', 'sys', 'whnd', 'win32api', 'win32con'] While you can dump the code objects from memory by following (6) it will not be directly decompilable as in super mode, the opcodes are remapped. Infact, Pyarmor has it's own implementation of the PyEval_EvalFrameDefault in pytransform.pyd function which executes the remapped opcodes. Additionally, in super modes the co_consts of each code object are moved to be the top level code object and stored in a tuple. The co_const is then replaced with integer indicating the index in the tuple where the code object was moved. the At runtime, pyarmor automatically refers to the correct code_object by following the index. Reconstructing the original pyc file by undoing (9) is a lot easier than (8). Depending on what you're looking for it may be enough to just dump the code object to get readable strings in addition to running the individual pyc files in a REPL. 4
rhythm Posted June 19, 2021 Author Posted June 19, 2021 kao and Extreme Coders. Thank you so much for your help and advice. I will try it.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now