AI智能
改变未来

Android第一代壳demo编写


Android第一代壳Demo编写

前言

这篇文章是对姜维大佬的这篇文章[Android中的Apk的加固(加壳)原理解析和实现]的补充。建议先看一编姜维大佬的这篇文章再看。

姜维大佬写那篇文章的时间距今已久,如果要按照他文章中的思路编写你需要Android4.4以下的环境也就是Dalvik虚拟机的环境。而且我在用Dv虚拟机进行试验时也运行不了。不明原因,现在猜想可能是当时抄的脚本问题。

这里讲一下我在Android5.0及以上环境下实现第一代壳demo过程中遇到的问题以及应对方法。

后面将待加壳APK称为源APK,壳程序称为壳APK,加壳后的APK称为加壳APK。

0x1 DEX的加载问题

首先遇到的是DEX的加载问题。在某个和Android版本升级之后,DexClassLoader的代码发生了改变。已经不可以直接加载Dex文件了。需要将Dex转化为jar或者apk进行加载,否则加载过程中取出来的Dex文件加载进去也没用,文件还会被删除掉。在attachBasecontext中尝试加载MainActivity的时候会报错。

  • 解决方法

将代码中的这句

DexClassLoader dcl = new DexClassLoader(apkName,dexPath,libPath,oldCl);

dcl的类型改为DexClassLoader的父类ClassLoader。

并使用dex2jar将源APK的DEX文件转为jar包,再用dx工具转为Android可以加载的jar包。具体命令为

dex2jar 源APK.apk -o target.jar -> 得到普通jar包dx --dex --output=target.jar target,jar  ->得到Android可加载的jar

拼接时就用得到的jar包代替源APK的Dex拼接。

0x2 资源加载问题

加载完Dex后遇到的就是资源加载的问题。因为姜维大佬的方法是将源APK的DEX拼接到壳APK的DEX后,在运行时在取出来进行加载。那么这个APK运行时的布局等资源是没有被打包过去的,只有代码。当壳APK将DEX取出并动态加载运行到加载布局文件的代码时就会出错。

网上有些文章提供的解决办法是直接将源APK的资源文件复制到壳APK中。但是不同的项目布局文件对应的id是不一样的,需要重新将资源的id编辑过于麻烦。

  • 解决方法

将拼接后的DEX文件放回源APK中就不用再自己动手去加载资源了。

0x3 AppCompatActivity问题

解决上面两个问题后加壳APK还是不能运行,报错中有对Android版本的描述如:

Before Android4.1…………balalbala

且报错中有Appcompat的字样

查资料发现这个AppCompatActivity是更换art虚拟机之后才有的。与普通的Activity不同。AppCompat的布局文件会在上方有个标题栏which is Activity所没有的。

  • 解决方法

目前只想到将源APK的Activity继承的父类全部改为Activity。毕竟这只是一个小demo,不是重点。以后找到方法再进行改进。

0x4 Dex拼接后Dex头的修复问题

若不修复Dex头,Dex文件是不会被正确识别的。之前拼接Dex所使用的脚本是拿网上的。

这样子并不能修复SHA1字段和CheckSum字段,还会覆盖掉文件大小字段。因为这样子写实际写进去的是得到SHA1对应的ASCII码的十六进制数。得不到正确的Dex文件。

  • 解决方法

这个是我修改过的脚本

import binasciiimport osimport hashlibimport zlibimport zipfileimport shutilfrom xml.dom import minidomunshellpath = r\"D:\\CodeAS\\shell1\\app\\build\\outputs\\apk\\debug\\app-debug.apk\"srcapkpath = r\"D:\\CodeAS\\srcapk\\app\\build\\outputs\\apk\\debug\\app-debug.apk\"def fixCheckSum(shell):shell.seek(0x0C)data = shell.read()checksum = zlib.adler32(data)strchecksum = str(hex(checksum))strchecksum = strchecksum.replace(\"0x\", \"\")if not len(strchecksum) % 2 == 0:strchecksum = strchecksum.zfill(len(strchecksum)+1)shell.seek(0x08)shell.write(bytearray.fromhex(strchecksum)[::-1])def fixSHA1(shell):shell.seek(0x20)signBytes = shell.read()sha1 = hashlib.sha1()sha1.update(signBytes)sign = sha1.hexdigest()b = bytes.fromhex(sign)shell.seek(0x0C)shell.write(b)def fixFileSize(shell, num):b = bytearray()for i in range(4):number = int(num % 256)b.append(number)num = num >> 8shell.seek(0x20)shell.write(b)def IntToHex(num):b = bytearray()for i in range(4):number = int(num % 256)b.append(number)num = num >> 8b.reverse()return bdef fixXML()56c:doc = minidom.parse(srcapkpath.replace(\'.apk\', r\'\\AndroidManifest.xml\'))root = doc.documentElementapp_node = root.getElementsByTagName(\'application\')[0]app_name = app_node.getAttribute(\'android:name\')app_node.setAttribute(\'android:name\', \'app.simba.shell1.ShellApplication\')meta = doc.createElement(\'meta-data\')meta.setAttribute(\'android:name\', \'ApplicationName\')meta.setAttribute(\'android:value\', app_name)app_node.appendChild(meta)doc.writexml(open(srcapkpath.replace(\'.apk\', r\'\\AndroidManifest.xml\'), \'w\'), encoding=\'utf-8\')def main():if os.path.exists(unshellpath.replace(\'.apk\',\'\')):shutil.rmtree(unshellpath.replace(\'.apk\',\'\'))apk = zipfile.ZipFile(unshellpath)for n in apk.namelist():apk.extract(n,unshellpath.replace(\'.apk\',\'\'))for root, dirs, files in os.walk(unshellpath.replace(\'.apk\', \'\')):for f in files:if os.path.join(root, f).endswith(\'.dex\'):unshelldexpath = os.path.join(root, f)print(\'[*] 成功反编译shellAPK\')os.system(\'apktool.bat -f -s d \' + srcapkpath +\' -o \' + srcapkpath.replace(\'.apk\', \'\'))srcjarpath = srcapkpath.replace(\'.apk\',\'.jar\')os.system(\'dex2jar.bat -f \'+srcapkpath+\' -o \'+srcjarpath)os.system(\'java -jar dx.jar --dex --output=\'+srcjarpath+\' \'+srcjarpath)ad8for root, dirs, files in os.walk(srcapkpath.replace(\'.apk\', \'\')):for f in files:if os.path.join(root, f).endswith(\'.dex\'):os.remove(os.path.join(root, f))print(\'[*] 成功反编译待加壳APK\')unshelldex = open(unshelldexpath, \'rb+\')tmpshell = unshelldex.read()unshellArray = bytearray(tmpshell)unshelldex.close()print(\'[*] 成功读取脱壳DEX文件\')tmp = open(srcjarpath,\'rb+\')srcjarArray = bytearray(tmp.read())tmp.close()tmpdex = unshellArray +srcjarArray+IntToHex(len(srcjarArray))f = open(srcapkpath.replace(\'.apk\',\'/classes.dex\'),\'wb+\')f.write(tmpdex)f.close()shell = open(srcapkpath.replace(\'.apk\',\'/classes.dex\'),\'rb+\')fixFileSize(shell, len(tmpdex))fixSHA1(shell)fixCheckSum(shell)shell.close()print(\'[*] DEX文件修复完毕\')fixXML()print(\'[*] AndroidManifest文件修改完毕\')outputpath = os.path.dirname(srcapkpath)+r\'\\shell_\'+os.path.basename(srcapkpath)os.system(\'apktool.bat  b \'+srcapkpath.replace(\'.apk\', \'\')+\' -o \' +outputpath)print(\'[*] APK重打包完毕\')os.system(\'jarsigner -keystore \'+r\'D:\\CodeAS\\mykey.jks\'+\' -signedjar \' +outputpath.replace(\'.apk\', \'_signed.apk\')+\' \'+outputpath+\' mykey \'+\'-storepass 123456\')if __name__ == \"__main__\":main()
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android第一代壳demo编写