【Android编程实战】利用apktool编写apk的dex加壳工具
/作者:Kali_MG1937
CSDN博客:ALDYS4
QQ:3496925334/
最近初学dex加固方面的内容
突发奇想想自己做一个自动化的脚本工具
DEX加壳原理
将源apk与脱壳用的dex合并为一个加密后的dex
修改加密后dex的头信息和长度信息等
让app在启动时先运行加密后dex的脱壳代码
释放源apk
利用动态class加载源app的代码
加壳过程详细看:
https://blog.csdn.net/jiangwei0910410003/article/details/48415225
构建思路
要如何让加固后的apk先运行脱壳代码?
我们知道AndroidManifest.xml文件中application的name属性指定的类会在所有类之前最先被执行
例如
<application android:name=".Main">
</application>
那么app运行时就会最先执行Main中的代码
但Main必须继承Application
那么实现起来就简单了
先将apk和脱壳dex合并为加密后的dex,我先将它命名为classes_encode.dex
再反编译源apk
DEX脱壳类为ProxyApplication
那么就要修改源apk的AndroidManifest.xml中application的name属性为ProxyApplication
修改完成后回编译apk
解压回编译后的apk
删除源apk内的classes.dex并替换为加密后的dex
(重命名classes_encode.dex为classes.dex并放入源apk)
打包apk
实现项目
apk反编译我已经在我上次写的博客里实现了
https://blog.csdn.net/ALDYS4/article/details/85998037
/*
apktool即为AndroidApktool对象
详细看我上面发的博客
*/
//反编译
public void apkDecompile(final String file,final String outfile) throws InterruptedException{
try
{
append("\n反编译:" + file);
apktool = new AndroidApktool();//初始化apktool
apktool.initAndroid(MainActivity.this);
File out=new File("/sdcard/decode_dex/" + outfile);
if (!out.isDirectory())
{
out.mkdirs();
}
Cmd cmds=new Cmd();
cmds.apkDecompile(file, "/sdcard/decode_dex/" + outfile);
String[] command=cmds.get();
append("\n反编译执行:" + cmds.getString().replace("\n", " "));
apktool.run(command);//执行命令
}
catch (InterruptedException e)
{append("\n反编译Interrupted异常:"+e.toString());
}
catch (BrutException e)
{append("\n反编译Burt异常:"+e.toString());
}
catch (IOException e)
{append("\n反编译IO异常:"+e.toString());
}
}
//回编译
public void apkBackcompile(final String appfile) throws InterruptedException{
try
{
append("\n开始回编译:" + appfile);
String errorCode=readFile(BaseFile + appfile + "/AndroidManifest.xml");
errorCode = errorCode.replace("android:resizeableActivity=\"true\"", "");
writeFile(BaseFile + appfile + "/AndroidManifest.xml", errorCode);
Cmd cmd=new Cmd();
String aapt=Manage.copyfile(MainActivity.this, "aapt.mrp");
//String aapt=apktool.extra("aapt.mrp",BaseFile).toString();
cmd.apkCompile(BaseFile + appfile, aapt, BaseFile + appfile + ".apk");
String[] command=cmd.get();
append("\n回编译执行:" + cmd.getString().replace("\n", " "));
apktool.run(command);
append("\n回编译完成");
}
catch (InterruptedException e)
{append("\n回编译Interrupted异常:"+e.toString());
}
catch (BrutException e)
{append("\n回编译Burt异常:"+e.toString());
}
catch (IOException e)
{append("\n回编译IO异常:"+e.toString());
}
}
先利用apkDecompile方法反编译apk
接下来就是application注入
即为修改application的name属性
我这里利用了dom4j来解析xml
//注入application
public void applicationInject() throws IOException, DocumentException{
//AndroidManifest
String AndroidManifest=readFile(BaseFile+"app_src/AndroidManifest.xml");
Document doc=DocumentHelper.parseText(AndroidManifest);
Element ele=doc.getRootElement().element("application");
String name=ele.attributeValue("name");//获取application的name参数
if(name==null){//当application不包含name参数时
writeFile(BaseFile+"app_src/AndroidManifest.xml",
AndroidManifest.replace("<application","<application android:name=\"com.dex.ProxyApplication\""));
}
else
{
String name_="android:name=\""+name+"\"";
AndroidManifest=AndroidManifest.replace(name_,"");
AndroidManifest=AndroidManifest.replace("<application","<application android:name=\"com.dex.ProxyApplication\"");
AndroidManifest=AndroidManifest.replace("</application>","<meta-data android:name=\"APPLICATION_CLASS_NAME\" android:value=\""+name+"\"/>");
writeFile(BaseFile+"app_src/AndroidManifest.xml",AndroidManifest);
}
append("\napplication已完成注入");
}
注入完成后利用apkBackcompile方法打包为apk
接下来就是解压apk
//文件解压
public void zipEntry(boolean assets,String zipfile,String out,String refer) throws IOException{
append("\n开始解压文件");
boolean entry_=true;
File file=null;
if(assets==true){
file=apktool.extra(zipfile,BaseFile);
append("路径:"+file.getPath());
}
else{
file=new File(zipfile);
}
ZipFile zip=new ZipFile(file);
ZipInputStream zin=new ZipInputStream(new FileInputStream(file));
ZipEntry entry ;
while (((entry=zin.getNextEntry())!=null)){//如果entry不为空
if(!refer.equals("")){//若要指定的文件名不为空
if(entry.getName().equals(refer)){
entry_=true;
}else{entry_=false;}
}else{entry_=true;}
if(entry_){
File tmp = new File(out+entry.getName());//解压出的文件路径
if(entry.isDirectory()){
tmp.mkdirs();
}
if (!tmp.exists()){//如果文件不存在
tmp.getParentFile().mkdirs();//创建文件父类文件夹路径
InputStream fis = zip.getInputStream(entry);
OutputStream fos = new FileOutputStream(tmp);
byte []bys = new byte[1024];
int len = 0;
while((len = fis.read(bys)) != -1){
fos.write(bys,0,len);
}
fos.close();
fis.close();
}
zin.closeEntry();
}//if entry_
}
zin.close();
append("\n解压文件完成");
}
解压完成
在替换dex前先对脱壳dex进行修改
我先释放含有脱壳dex的apk
对其进行反编译
再对ProxyApplication.smali代码中需要动态加载的class替换成源apk的主类
//替换壳启动活动
public void replaceAct(String com) throws IOException{
String smali=readFile(BaseFile+"decode_app_src/smali/com/dex/ProxyApplication.smali");
smali=smali.replace("com.test.MainActivity",com);
writeFile(BaseFile+"decode_app_src/smali/com/dex/ProxyApplication.smali",smali);
append("\n活动注入完成");
}
回编译含有脱壳dex的apk
提取其dex
接下来就是加固dex了
因为我在文章开头贴上了加固dex方法的链接
这里就不贴代码了
加固完成后替换dex
再压缩为apk
//压缩文件
public void zip(String zipfile,File input) throws FileNotFoundException, IOException{
append("\n开始打包");
ZipOutputStream out=new ZipOutputStream(new FileOutputStream(zipfile));
zip(out,input,"");
out.close();
}
public void zip(ZipOutputStream out,File f,String base) throws IOException{
if(f.isDirectory()){
File[] fl=f.listFiles();
if(base.length()!=0){
out.putNextEntry(new ZipEntry((base+"/")));
//System.out.println(base+"/");
}
for(int i=0;i<fl.length;i++){
zip(out,fl[i],fl[i].getPath().replace(BaseFile+"app_dex/",""));
System.out.println(base+"/");
}
}
else{
out.putNextEntry(new ZipEntry(base));
FileInputStream in=new FileInputStream(f);
int b;
while((b=in.read())!=-1){
out.write(b);
}
in.close();
}
}
方法差不多都写上了
接下来贴加固过程
public class encoding extends AsyncTask<Void,Void,Void>
{
@Override
protected Void doInBackground(Void[] p1)
{
try
{
append("\n框架准备");
//installFrame();
append("\nApktool反编译开始");
apkDecompile(ed.getText().toString(),"app_src");
//反编译输出位置/sdcard/decode_dex/app_src
append("\n反编译完成,开始application注入");
applicationInject();
//zipEntry(true,"decode_app_src.zip",BaseFile,"");
//解压解壳dex文件
String dex_app=apktool.extra("decode_app.apk",BaseFile).toString();
//释放壳文件
apkDecompile(dex_app,"decode_app_src");
//反编译壳文件
showInject();
lock();
replaceAct(com);
//替换壳启动活动
apkBackcompile("decode_app_src");
//回编译壳文件
zipEntry(false,BaseFile+"decode_app_src.apk",BaseFile+"decode_dex/","classes.dex");
//解压壳文件dex
apkBackcompile("app_src");
//回编译源app
zipEntry(false,BaseFile+"app_src.apk",BaseFile+"app_dex/","");
//解压源app
dex.encode("decode_dex/classes.dex","app_src.apk");
append("\nclasses.dex加固完成");
//dex加固到/sdcard/decode_dex/classes.dex
File f=new File(BaseFile+"app_dex/classes.dex");
f.delete();
append("\n"+f+"删除完成");
//删除源app的dex文件
copy(BaseFile+"classes.dex",BaseFile+"app_dex/classes.dex");
//复制加壳后的dex至源app
zip(BaseFile+"encode.apk",new File(BaseFile+"app_dex"));
//打包app
append("\n打包结束");
}
catch (InterruptedException e)
{}
catch (DocumentException e)
{append("\nDocument异常:"+e.toString());
}
catch (IOException e)
{append("\nIO异常:"+e.toString());
}
return null;
}
编译完成,放图
那么试试对msf载荷加固
上传至在线木马扫描网站
加固前:23个杀毒引擎报毒
加固后:3个引擎报毒,免杀效果还不错嘛
开源放上:
https://pan.baidu.com/s/1klERbmiDRk3Ipo2tlapa7A
上一篇: 安卓逆向-加壳检测