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 binascii
import os
import hashlib
import zlib
import zipfile
import shutil
from xml.dom import minidom
unshellpath = 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 >> 8
shell.seek(0x20)
shell.write(b)
def IntToHex(num):
b = bytearray()
for i in range(4):
number = int(num % 256)
b.append(number)
num = num >> 8
b.reverse()
return b
def fixXML():
doc = minidom.parse(srcapkpath.replace('.apk', r'\AndroidManifest.xml'))
root = doc.documentElement
app_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)
for 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()
Android第一代壳demo编写的更多相关文章
- Android JNI学习(五)——Demo演示
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- 解决Android微信支付官方demo运行失败
Android微信支付官方demo运行失败,在此简单记录一下解决步骤 1.httpclient错误 官方给的demo是eclipse的,打开之后提示httpclient的错误,我知道在as下解决htt ...
- Android SO UPX壳问题小记
网上有篇 Android SO(动态链接库)UPX加固指南,详细介绍了如何使用UPX给Android SO加壳,尝试做了一下结果ok,这里只记录遇到的几个小问题. 1.40k以下so不能加壳 kiii ...
- 【黑客免杀攻防】读书笔记15 - 源码免杀、C++壳的编写
1.源码免杀 1.1 定位产生特征的源码 定位文件特征 1.根据MyCCL的特征码定位工具,定位出有特征的地址 2.根据VS的反汇编窗口,输入有特征的地址得到特征地址与源码的关系 3.插入Messag ...
- Android之ViewPager循环Demo
ViewPager是谷歌官方提供的兼容低版本安卓设备的软件包,里面包含了只有在安卓3.0以上可以使用的api.Viewpager现在也算是标配了,如果一个App没有用到ViewPager感觉还是比较罕 ...
- Android studio百度地图demo出现230错误,key校验失败
转自daoxiaomianzi原文 Android studio 百度地图demo出现230错误,key校验失败 使用AndroidStudio导入Baidu地图的as版的demo,引入后,发现没有k ...
- Android APK加壳技术方案
Android APK加壳技术方案[1] Android APK加壳技术方案[2]
- 以C#编写的Socket服务器的Android手机聊天室Demo
内容摘要 1.程序架构 2.通信协议 3.服务器源代码 4.客户端源代码 5.运行效果 一.程序架构 在开发一个聊天室程序时,我们可以使用Socket.Remoting.WCF这些具有双向通信的协议或 ...
- Android -- 自定义View小Demo,动态画圆(一)
1,转载:(http://blog.csdn.NET/lmj623565791/article/details/24500107),现在如下图的效果: 由上面的效果图可以看到其实是一个在一个圆上换不同 ...
随机推荐
- MySQL性能分析show profiles详解
前言 前几篇文章我们讲了什么是 MySQL 索引,explain分析SQL语句是否用到索引,以及索引的优化等一系列的文章,今天我们来讲讲Show profiles,看看SQL耗时到底出现在哪个环节. ...
- Trick:如何去掉html标签点击时的蓝色边框
我们在用html标签时,如input.button.select,img标签时,点击标签经常出现一个蓝色的边框,这个边框真的很low,想要去掉怎么办 其实,css有样式可以设置一下,这个问题就轻松 ...
- Linux目录的慨念
1.路径 对文件进行访问时,要用到"路径"(Path)的慨念. 顾名思义,路径是指从树型目录中的某个目录层次到某个文件的一条道路. 此路径的主要构成是目录名称,中间用"/ ...
- 利用github使自己的域名绑定CSDN博客
首先你要有一个GitHub账号和一个域名 在你自己GitHub账号中创建一个仓库 在仓库里创建一个index.html的文件 <!DOCTYPE html> <html& ...
- GitHub基础使用指南
引言: 只要进行了一段时间的软件或者编程相关知识的学习,相信大家或多或少都会见过这只"猫猫",这可不是什么宠物店铺的商标,身为即将成为程序猿/媛的你,或者已经是一位程序猿/媛的你, ...
- openstack octavia的实现与分析(一)openstack负载均衡的现状与发展以及lvs,Nginx,Haproxy三种负载均衡机制的基本架构和对比
[负载均衡] 大量用户发起请求的情况下,服务器负载过高,导致部分请求无法被响应或者及时响应. 负载均衡根据一定的算法将请求分发到不同的后端,保证所有的请求都可以被正常的下发并返回. [主流实现-LVS ...
- 【十天自制软渲染器】DAY 01:图形学学习建议与环境搭建
推荐直接阅读博客原文,更新更及时,阅读体验更佳 「十天自制软渲染器」这个标题我承认标题党了.在对图形学一无所知的情况下想十天自制一个软渲染器,就好似一节课没上过却试图一个晚上看完<30 天精通 ...
- .NET Core部署到linux(CentOS)最全解决方案,常规篇
本文为大家介绍使用 .NET Core部署到Linux服务器的方法,通过本文你将了解到Linux在虚拟机下的安装.Xshell,Xftp的使用方法.git在linux下的交互使用以及.net core ...
- 汇编学习笔记——DOS及DEBUG介绍
转自:https://www.shiyanlou.com/courses/running/332 一.课程简介 声明:该课程基于<汇编语言(第2版)>郑晓薇 编著,机械工业出版社.本节实验 ...
- mysql—make_set函数
使用格式:MAKE_SET(bits,str1,str2,-) 1 返回一个设定值(含子字符串分隔字符串","字符),在设置位的相应位的字符串.str1对应于位0,str2到第1位 ...