blog.dornea.nu

Hack, code and drink some țuică. Personal blog of Victor Dorneanu.

Disect Android APKs like a Pro - Static code analysis

I've started writing this IPython notebook in order to make myself more comfortable with Android and its SDK. Due to some personal interests I thought I could also have a look at the available RE tools and learn more about their pros & cos. In particular I had a closer look at AndroGuard which seems to be good at:

I was charmed but its capabilities and the pythonic art of handling with APKs. In the 2nd step I've needed a malware to play it, so I had a look at Contagio Mobile. There I've randomly chosen a malware and got stucked with Fake Banker. There are some technical details about the malware itself gained during automated tests which can be read here.

This article will only deal with the static source code analysis of the malware. A 2nd part dedicated to the dynamic analysis is planed as well.

➜ ~ ssh kali.local
[email protected]'s password:
Linux kali 3.7-trunk-amd64 #1 SMP Debian 3.7.2-0+kali8 x86_64
The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Now you'll have to extract the archive. As mentioned on the site you'll have to contact the sites maintainer in order to get the password for the files. Thanks to @snowfl0w for providing me the password.

NOTE: The ordinary unzip command will fail to extract the files. You should install p7zip.

p10 is the 2nd argument given to getRawData. If you pay attention you may notice that if p10 == 1 nothing special will happen. Otherwise (content of blfs.key is read out) there are some string manipulations taking place. This took me a while to notice it and was the reason I couldn't decrypt the config.cfg. This is what it does with the content of blfs.key:

convert string to byte array

convert byte array to hex string

take only the first 50 bytes

In the end v6 will contain the decryption key. Using PyCrypto we'll try to decrypt the content in Python:

In [39]:

fromCrypto.CipherimportBlowfishfromCryptoimportRandomfromstructimportpackfrombinasciiimporthexlify,unhexlify# Read content from filesblfs_key=!cat output/res/raw/blfs.key
ciphertext_base64=!cat output/res/raw/config.cfg
ciphertext_raw=ciphertext_base64[0].decode("base64")# Some settingsIV="12345678"_KEY=blfs_key[0]ciphertext=ciphertext_raw# As seen in the source code: # * hex-encode the blfs key# * take only the substring[0:50]KEY=hexlify(_KEY)[:50]# Do the decryptioncipher=Blowfish.new(KEY,Blowfish.MODE_CBC,IV)message=cipher.decrypt(ciphertext)message

importurllib2# Get URLs from DataFrame (only the valid ones)urls=df['Value'][10:19].tolist()#urls = ["http://google.de/", "http://blog.dornea.nu/about"]resp={}defget_status_code(host,path="/"):""" This function retreives the status code of a website by requesting HEAD data from the host. This means that it only requests the headers. If the host cannot be reached or something else goes wrong, it returns None instead. """try:conn=httplib.HTTPConnection(host)conn.request("HEAD",path)returnconn.getresponse().getheaders()exceptStandardError:returnNone# Iterate through URLsforuinurls:p='(?:http.*://)?(?P<host>[^:/ ]+).?(?P<port>[0-9]*)/(?P<path>.*)'m=re.search(p,u)ifm:status_code=get_status_code(m.group('host'),"/"+m.group('path'))resp[u]=status_coderesp