Nqndi Posted September 9, 2021 Posted September 9, 2021 View File Titanium (Python Obfuscator) This is my first crackme, so I may have screwed up some things, but overall it should be a fun challenge. The file is protected with my own packer/obfuscator, Titanium, and compiled with pyinstaller. Read the readme before reversing. Submitter Nqndi Submitted 09/07/2021 Category CrackMe
Solution Extreme Coders Posted November 13, 2021 Solution Posted November 13, 2021 Correct Key: Spoiler jtr|Y@Z2oK:Mf|P8eMQT[,^#}7X0l~ WriteUp: Running crackme.exe prompts for a key and prints "Bad key!" if its incorrect. Spoiler As per the challenge description, this crackme is a PyInstaller generated executable. The "protection" is implemented in the C extension (native library) titanium.pyd which we can obtain after extracting with Pyinstxtractor. Decompiling crackme.pyc with uncompyle6, we get this code. # uncompyle6 version 3.8.0 # Python bytecode 3.7.0 (3394) # Decompiled from: Python 3.8.12 (default, Oct 17 2021, 23:37:02) [MSC v.1929 64 bit (AMD64)] # Embedded file name: crackme.py import lzma, base64 from titanium import __titanium_runtime__ from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import os, zlib __titanium_runtime__('linear', b'x\x9c%\xd79\xb2$!......[snip].....x9e\xf8', 10, False) # okay decompiling crackme.pyc The protection tries to mimic pyarmor in the sense that pyarmor also has a pyarmor_runtime function which is called with similar set of arguments. __titanium_runtime__ is defined in the C extension titanium.pyd. Python extensions are commonly written in C but can also be written in Python itself using Cython. The crackme follows the later approach. Cython "compiles" the python bytecode to native code which is generally difficult to RE than plain Python code. At this point it may look as if we've to analyze titanium.pyd to understand what its going on, but there easier ways. The fastest way Note the string "Bad key!". We can search for this string in memory of the crackme process. Since this is a pyinstaller generated executable there will be two crackme processes. We need to scan the child process, (with PID 5600 in the image below) Spoiler Searching for "Bad key!" we get to Spoiler A little above is the base64 encoded correct key anRyfFlAWjJvSzpNZnxQOGVNUVRbLF4jfTdYMGx+ which after decoding reveals the correct key. Spoiler The logical way Instead of searching for "Bad key!" in memory its also possible to have the crackme print its real code bypassing titanium entirely. Note the lzma, zlib imports at the top of crackme.py. Obfuscators commonly compress and then encrypt the code. Doing the other way doesn't make sense as encrypted code won't be compressible. This implies while executing its decryption followed by decompression. Python is a dynamic language which makes it possible to replace the implementation of any function at runtime. Using this we can "hook" lzma.decompress and check out the decompressed result. Spoiler # uncompyle6 version 3.8.0 # Python bytecode 3.7.0 (3394) # Decompiled from: Python 3.8.12 (default, Oct 17 2021, 23:37:02) [MSC v.1929 64 bit (AMD64)] # Embedded file name: crackme.py import lzma, base64 from titanium import __titanium_runtime__ from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import os, zlib original_lzma_decompress = lzma.decompress def lzma_decompress(x): d = original_lzma_decompress(x) print (d) return d lzma.decompress = lzma_decompress __titanium_runtime__('linear', b'x\x9c%\xd79\xb2$!......[snip].....x9e\xf8', 10, False) # okay decompiling crackme.pyc Before running the above code in Python 3.7, ensure that PyCryptoDome is installed and titanium.pyd is in the same directory as the crackme. Spoiler Base64 decoding the exec-d string "X0I9YidVbXhHY1Z........WNsYXJlX18oKQ==", we get this minified Python code. Spoiler _B=b'UmxGcVQxSlZXbU5tV2tvellVNTRSRE13TUVabFExWXpWVXRJYmxCbE1Xdz0=' _A=b'WnZhSzhsSGFlaEdkamUyZFlhWmN3THAxdldKWlZyQlI=' import base64 asd=b'RlFqT1JVWmNmWkozYU54RDMwMEZlQ1YzVUtIblBlMWw=' asda=_A asdaa=b'V25aaFN6aHNTR0ZsYUVka2FtVXlaRmxoV21OM1RIQXhkbGRLV2xaeVFsST0=' asdaaa=_B asdaaaa=b'YXNkYWFhYWFhYWFhYWE=' asdaaaaa=b'anRyfFlAWjJvSzpNZnxQFGVNUVRbLF4jfTdYMGx=' asdaaaaaa=b'b2hfaG93X2RpZF95b3VfZmluZF90aGVfa2V5Pw==' asdaaaaaaa=b'RlFqT1JVFmNmWkozYU54RDMwMEZlQ1QzVUtIblBHMWw=' asdaaaaaaaa=_A asdaaaaaaaaa=b'Q25aaFN6aHNTR0ZsYUVka2FtVXlaRmXoV21OM1fIQXhkbGRLV2xaeVFsST0=' asdaaaaaaaaaa=_B asdaaaaaaaaaaa=b'VXNkYWFhYWFhYWFAWb5tj43HTK2h' asdaaaaaaaaaaaa=b'FlAWjJvSzpNZnxQOGVNUVRbLF4FlAWjJvSzpNZnxQOGVNUVRbLF4' asdaaaaaaaaaaaaa=b'abFExWXpWVXRJYmabFExWXpWVXRJYmabFExWXpWVXRJYm' asdaaaaaaaaaaaaaa=b'V2VsY29tZSEgUGxlYXNlIGVudGVyIHlvdXIga2V5OiA=' try:exec(asd) except:pass try:exec(asda) except:pass try:exec(asdaa) except:pass try:exec(asdaaa) except:pass try:exec(asdaaaa) except:pass try:exec(asdaaaaaaaaaaaa) except:pass class __declare__: def __init__(self):self.start() def start(self):done=False;exec(f"print('{base64.b64decode(asdaaaaaaaaaaaaaa).decode()}')");exec("k = input('')");exec(f"if k == '{base64.b64decode('anRyfFlAWjJvSzpNZnxQOGVNUVRbLF4jfTdYMGx+').decode()}':\n print('Congrats! You cracked the program! :D')\nelse:print('Bad key!')");input('') if __name__=='__main__':__declare__() which after formatting comes to Spoiler _B = b"UmxGcVQxSlZXbU5tV2tvellVNTRSRE13TUVabFExWXpWVXRJYmxCbE1Xdz0=" _A = b"WnZhSzhsSGFlaEdkamUyZFlhWmN3THAxdldKWlZyQlI=" import base64 asd = b"RlFqT1JVWmNmWkozYU54RDMwMEZlQ1YzVUtIblBlMWw=" asda = _A asdaa = b"V25aaFN6aHNTR0ZsYUVka2FtVXlaRmxoV21OM1RIQXhkbGRLV2xaeVFsST0=" asdaaa = _B asdaaaa = b"YXNkYWFhYWFhYWFhYWE=" asdaaaaa = b"anRyfFlAWjJvSzpNZnxQFGVNUVRbLF4jfTdYMGx=" asdaaaaaa = b"b2hfaG93X2RpZF95b3VfZmluZF90aGVfa2V5Pw==" asdaaaaaaa = b"RlFqT1JVFmNmWkozYU54RDMwMEZlQ1QzVUtIblBHMWw=" asdaaaaaaaa = _A asdaaaaaaaaa = b"Q25aaFN6aHNTR0ZsYUVka2FtVXlaRmXoV21OM1fIQXhkbGRLV2xaeVFsST0=" asdaaaaaaaaaa = _B asdaaaaaaaaaaa = b"VXNkYWFhYWFhYWFAWb5tj43HTK2h" asdaaaaaaaaaaaa = b"FlAWjJvSzpNZnxQOGVNUVRbLF4FlAWjJvSzpNZnxQOGVNUVRbLF4" asdaaaaaaaaaaaaa = b"abFExWXpWVXRJYmabFExWXpWVXRJYmabFExWXpWVXRJYm" asdaaaaaaaaaaaaaa = b"V2VsY29tZSEgUGxlYXNlIGVudGVyIHlvdXIga2V5OiA=" try: exec(asd) except: pass try: exec(asda) except: pass try: exec(asdaa) except: pass try: exec(asdaaa) except: pass try: exec(asdaaaa) except: pass try: exec(asdaaaaaaaaaaaa) except: pass class __declare__: def __init__(self): self.start() def start(self): done = False exec(f"print('{base64.b64decode(asdaaaaaaaaaaaaaa).decode()}')") exec("k = input('')") exec( f"if k == '{base64.b64decode('anRyfFlAWjJvSzpNZnxQOGVNUVRbLF4jfTdYMGx+').decode()}':\n print('Congrats! You cracked the program! :D')\nelse:print('Bad key!')" ) input("") if __name__ == "__main__": __declare__() The correct key is in the last exec-d string as we also found earlier. 5 3
Sean the hard worker Posted January 11, 2022 Posted January 11, 2022 good reversing skill of yours. good for you. thank you for your explanation. regards. sean. 1
4dsec Posted April 5, 2022 Posted April 5, 2022 On 11/13/2021 at 12:56 PM, Extreme Coders said: Correct Key: Hide contents jtr|Y@Z2oK:Mf|P8eMQT[,^#}7X0l~ WriteUp: Running crackme.exe prompts for a key and prints "Bad key!" if its incorrect. Hide contents As per the challenge description, this crackme is a PyInstaller generated executable. The "protection" is implemented in the C extension (native library) titanium.pyd which we can obtain after extracting with Pyinstxtractor. Decompiling crackme.pyc with uncompyle6, we get this code. # uncompyle6 version 3.8.0 # Python bytecode 3.7.0 (3394) # Decompiled from: Python 3.8.12 (default, Oct 17 2021, 23:37:02) [MSC v.1929 64 bit (AMD64)] # Embedded file name: crackme.py import lzma, base64 from titanium import __titanium_runtime__ from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import os, zlib __titanium_runtime__('linear', b'x\x9c%\xd79\xb2$!......[snip].....x9e\xf8', 10, False) # okay decompiling crackme.pyc The protection tries to mimic pyarmor in the sense that pyarmor also has a pyarmor_runtime function which is called with similar set of arguments. __titanium_runtime__ is defined in the C extension titanium.pyd. Python extensions are commonly written in C but can also be written in Python itself using Cython. The crackme follows the later approach. Cython "compiles" the python bytecode to native code which is generally difficult to RE than plain Python code. At this point it may look as if we've to analyze titanium.pyd to understand what its going on, but there easier ways. The fastest way Note the string "Bad key!". We can search for this string in memory of the crackme process. Since this is a pyinstaller generated executable there will be two crackme processes. We need to scan the child process, (with PID 5600 in the image below) Hide contents Searching for "Bad key!" we get to Hide contents A little above is the base64 encoded correct key anRyfFlAWjJvSzpNZnxQOGVNUVRbLF4jfTdYMGx+ which after decoding reveals the correct key. Hide contents The logical way Instead of searching for "Bad key!" in memory its also possible to have the crackme print its real code bypassing titanium entirely. Note the lzma, zlib imports at the top of crackme.py. Obfuscators commonly compress and then encrypt the code. Doing the other way doesn't make sense as encrypted code won't be compressible. This implies while executing its decryption followed by decompression. Python is a dynamic language which makes it possible to replace the implementation of any function at runtime. Using this we can "hook" lzma.decompress and check out the decompressed result. Hide contents # uncompyle6 version 3.8.0 # Python bytecode 3.7.0 (3394) # Decompiled from: Python 3.8.12 (default, Oct 17 2021, 23:37:02) [MSC v.1929 64 bit (AMD64)] # Embedded file name: crackme.py import lzma, base64 from titanium import __titanium_runtime__ from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import os, zlib original_lzma_decompress = lzma.decompress def lzma_decompress(x): d = original_lzma_decompress(x) print (d) return d lzma.decompress = lzma_decompress __titanium_runtime__('linear', b'x\x9c%\xd79\xb2$!......[snip].....x9e\xf8', 10, False) # okay decompiling crackme.pyc Before running the above code in Python 3.7, ensure that PyCryptoDome is installed and titanium.pyd is in the same directory as the crackme. Hide contents Base64 decoding the exec-d string "X0I9YidVbXhHY1Z........WNsYXJlX18oKQ==", we get this minified Python code. Reveal hidden contents _B=b'UmxGcVQxSlZXbU5tV2tvellVNTRSRE13TUVabFExWXpWVXRJYmxCbE1Xdz0=' _A=b'WnZhSzhsSGFlaEdkamUyZFlhWmN3THAxdldKWlZyQlI=' import base64 asd=b'RlFqT1JVWmNmWkozYU54RDMwMEZlQ1YzVUtIblBlMWw=' asda=_A asdaa=b'V25aaFN6aHNTR0ZsYUVka2FtVXlaRmxoV21OM1RIQXhkbGRLV2xaeVFsST0=' asdaaa=_B asdaaaa=b'YXNkYWFhYWFhYWFhYWE=' asdaaaaa=b'anRyfFlAWjJvSzpNZnxQFGVNUVRbLF4jfTdYMGx=' asdaaaaaa=b'b2hfaG93X2RpZF95b3VfZmluZF90aGVfa2V5Pw==' asdaaaaaaa=b'RlFqT1JVFmNmWkozYU54RDMwMEZlQ1QzVUtIblBHMWw=' asdaaaaaaaa=_A asdaaaaaaaaa=b'Q25aaFN6aHNTR0ZsYUVka2FtVXlaRmXoV21OM1fIQXhkbGRLV2xaeVFsST0=' asdaaaaaaaaaa=_B asdaaaaaaaaaaa=b'VXNkYWFhYWFhYWFAWb5tj43HTK2h' asdaaaaaaaaaaaa=b'FlAWjJvSzpNZnxQOGVNUVRbLF4FlAWjJvSzpNZnxQOGVNUVRbLF4' asdaaaaaaaaaaaaa=b'abFExWXpWVXRJYmabFExWXpWVXRJYmabFExWXpWVXRJYm' asdaaaaaaaaaaaaaa=b'V2VsY29tZSEgUGxlYXNlIGVudGVyIHlvdXIga2V5OiA=' try:exec(asd) except:pass try:exec(asda) except:pass try:exec(asdaa) except:pass try:exec(asdaaa) except:pass try:exec(asdaaaa) except:pass try:exec(asdaaaaaaaaaaaa) except:pass class __declare__: def __init__(self):self.start() def start(self):done=False;exec(f"print('{base64.b64decode(asdaaaaaaaaaaaaaa).decode()}')");exec("k = input('')");exec(f"if k == '{base64.b64decode('anRyfFlAWjJvSzpNZnxQOGVNUVRbLF4jfTdYMGx+').decode()}':\n print('Congrats! You cracked the program! :D')\nelse:print('Bad key!')");input('') if __name__=='__main__':__declare__() which after formatting comes to Reveal hidden contents _B = b"UmxGcVQxSlZXbU5tV2tvellVNTRSRE13TUVabFExWXpWVXRJYmxCbE1Xdz0=" _A = b"WnZhSzhsSGFlaEdkamUyZFlhWmN3THAxdldKWlZyQlI=" import base64 asd = b"RlFqT1JVWmNmWkozYU54RDMwMEZlQ1YzVUtIblBlMWw=" asda = _A asdaa = b"V25aaFN6aHNTR0ZsYUVka2FtVXlaRmxoV21OM1RIQXhkbGRLV2xaeVFsST0=" asdaaa = _B asdaaaa = b"YXNkYWFhYWFhYWFhYWE=" asdaaaaa = b"anRyfFlAWjJvSzpNZnxQFGVNUVRbLF4jfTdYMGx=" asdaaaaaa = b"b2hfaG93X2RpZF95b3VfZmluZF90aGVfa2V5Pw==" asdaaaaaaa = b"RlFqT1JVFmNmWkozYU54RDMwMEZlQ1QzVUtIblBHMWw=" asdaaaaaaaa = _A asdaaaaaaaaa = b"Q25aaFN6aHNTR0ZsYUVka2FtVXlaRmXoV21OM1fIQXhkbGRLV2xaeVFsST0=" asdaaaaaaaaaa = _B asdaaaaaaaaaaa = b"VXNkYWFhYWFhYWFAWb5tj43HTK2h" asdaaaaaaaaaaaa = b"FlAWjJvSzpNZnxQOGVNUVRbLF4FlAWjJvSzpNZnxQOGVNUVRbLF4" asdaaaaaaaaaaaaa = b"abFExWXpWVXRJYmabFExWXpWVXRJYmabFExWXpWVXRJYm" asdaaaaaaaaaaaaaa = b"V2VsY29tZSEgUGxlYXNlIGVudGVyIHlvdXIga2V5OiA=" try: exec(asd) except: pass try: exec(asda) except: pass try: exec(asdaa) except: pass try: exec(asdaaa) except: pass try: exec(asdaaaa) except: pass try: exec(asdaaaaaaaaaaaa) except: pass class __declare__: def __init__(self): self.start() def start(self): done = False exec(f"print('{base64.b64decode(asdaaaaaaaaaaaaaa).decode()}')") exec("k = input('')") exec( f"if k == '{base64.b64decode('anRyfFlAWjJvSzpNZnxQOGVNUVRbLF4jfTdYMGx+').decode()}':\n print('Congrats! You cracked the program! :D')\nelse:print('Bad key!')" ) input("") if __name__ == "__main__": __declare__() The correct key is in the last exec-d string as we also found earlier. what program do you use to edit hex?
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