天天看点

安卓刷量技术揭秘(二) 高级攻防技巧

估计厂商也认识到了APP在JAVA环境下运行收集到的信息被篡改的几率很大,所以很多厂商把目光标准了JNI层。

JNI层的运算是独立于JAVA层以外的、更底层的运算,也就是说,你用XPOSED安装了一个JAVA层的钩子修改了机器的重要信息A,但从JNI层读取信息A的时候,并不会触发你的钩子,所以返回值是系统真实值。

那么JNI层的数据如何修改呢?如何进入JNI层?进入JNI层后如何使用钩子?

这里我提供两个思路 两个思路都有完整的JAVA+JNI层的HOOK功能和框架 而且两种方案都有自己的优缺点,靠你自己取舍。

1 Cydia Substrate

具体可以参考

http://www.cydiasubstrate.com/

http://www.cydiasubstrate.com/inject/dalvik/ (java层支持)

http://www.cydiasubstrate.com/inject/android/ (JNI/Native层支持)

cydia框架的优势很明显,同时支持jni和java层的HOOK功能,缺点也很明显,兼容性不好,官网上标注最高支持安卓4.3版,但实际中测试发现一些第三方的ROM会出现一些莫名其妙的问题,而据说有人用安卓原生5.0也可以完美运行。所以是否使用CYDIA框架,要根据你目前的硬件情况来选择,如果你的手机ROM可以运行CYDIA,我更推荐使用这种完整的集成方案

使用cydia开发 搭建框架我这里不多重复了,可以登录官网查看文档。

public class Main
{
	public static int nTest=0;
	static void initialize()
	{
		//函数入口
		//do something........
	}
}
           

根据cydia和xposed的框架原理来看,cydia的开发成本会更高一些。

在你第一次使用这个框架时,编译后会出现很多莫名其妙的问题,如果你之前在c/c++语言上有过比较深的基础并且做过hook的相关项目,这些莫名其妙的问题解决起来会很轻松.

其中有三个点需要注意:

① cydia中的所有变量都是全局变量,例如,你在App A中个修改了nTest,那么会同步到其他App进程所有对Main.nTest的引用上。

② cydia中因为无法直接获得当前App的信息,你需要使用一个聪明的方式获得你想要东西作为你是否要HOOK目标进程的判断依据。

③ cydia引入了hookClassLoad方法,这个和xposed中的设计理念是不同的,详细资料看文档。

例如你可以用cydia hook捕获到Activity的OnCreate创建过程

Class<?> resources = android.app.Activity.class;
Method method_name=HTool.GetDeclaredMethodFromClass(resources,"onCreate",Bundle.class);
if (method_name != null) {
	final MS.MethodPointer old = new MS.MethodPointer();
	MS.hookMethod(resources, method_name, new MS.MethodHook() {
		@Override
		public Object invoked(Object obj, Object... args) throws Throwable {
			Object result_old = old.invoke(obj, args);
			Log.i(CreateTag,"onCreate PackName:"+PackName);
			return result_old;
		}
	}, old);
}
           

也可以使用cydia jni hook拦截getprop的返回值

int (*old__system_property_read)(int pi, char *name, char *value);
int new__system_property_read(int pi, char *name, char *value)
{
    int result=old__system_property_read(pi,name,value);
    return result;
}
//程序入口
MSInitialize
{
	MSImageRef image = MSGetImageByName("/system/lib/libc.so");
	if (image != NULL)
	{
		void *hook__system_property_read=MSFindSymbol(image,"__system_property_read");
		if(hook__system_property_read) MSHookFunction(hook__system_property_read,(void*)&new__system_property_read,(void **)&old__system_property_read);
		else LOGE("error find __system_property_read ");
	}
}
           

上面的例子HOOK了native层的__system_property_read方法,重定向到new__system_property_read。该方法是执行命令getprop的关键过程,配置好代码可以修改getprop的所有返回值

2 Xposed+Libc_hook.so

下面说第二种方案,如果你的安卓系统不能安装cydia 但同时又需要jni层的HOOK 那么我推荐你使用下面这套方案,实现成本相对第一种要复杂一些,但是跟着我所写的步骤,一定能得到你所要的功能。

在不能安装cydia的情况下如何进入jni层HOOK呢?很简单,找到关键so库文件,以病毒修改elf的方式,加载自己的注入代码,把自己的hook.so注入到该进程中即可

首先找到一个注入点,这里我推荐libc.so 因为基本上每个进程都要用到libc,更重要的是 libc的加载时机足够早。

① abd pull /system/lib/libc.so 先把手机上的libc.so到本地 然后使用ida打开

② 打开Function Window窗口找到dlopen函数的地址 0xEAD0

安卓刷量技术揭秘(二) 高级攻防技巧

③ 打开Program Segmentation窗口找到.init_array的地址 0x57480

安卓刷量技术揭秘(二) 高级攻防技巧

④ 按ALT+B搜索80个0 此地址随意 本例中找到地址为 0x51F00

安卓刷量技术揭秘(二) 高级攻防技巧

⑤ 按照上面的三个数字 对应带入下面的代码中 使用vs2015 c#编译通过

⑥ 运行时 把libc.so放在exe同目录下可得到libc_ok.so

使用上面的程序生成的libc_ok.so覆盖回/system/lib/libc.so即可

以后每当程序运行,都会自动加载/system/lib/libckis.so库文件 可以尽情的HOOK了!

class My
{
	static int dlopenAddr = 0xEAD0; 
	static int InitArrayAddr = 0x57480-0x1000; 
	static int asmAddr = 0x51F00; 
	
	static byte[] asm = { 0xFF,0x40,0x2D,0xE9,0x18,0x00,0x9F,0xE5,0x00,0x10,0xA0,0xE3,0x00,0x00,0x8F,0xE0,
0x01,0xF7,0xFF,0xEB,0xFF,0x40,0xBD,0xE8,0x08,0x00,0x9F,0xE5,0x00,0x00,0x8F,0xE0,		0x10,0xFF,0x2F,0xE1,0x9C,0x68,0x03,0x00,0x09,0xBF,0x01,0x00,0x00,0x00,0x00,0x00};
	static int StrAddr = asmAddr + 0x40;
	static void Main(string[] args)
	{
		BinaryReader br = null;
		try
		{
			br = new BinaryReader(new FileStream("libc.so", FileMode.Open));
		}
		catch (IOException e)
		{
			Console.WriteLine(e.Message + "Cannot open file.");
			return;
		}
		int length = Convert.ToInt32(br.BaseStream.Length);
		byte[] data = br.ReadBytes(length);
		br.Close();
		byte[] LibCkisSo = System.Text.Encoding.ASCII.GetBytes("libckis.so");
		Array.ConstrainedCopy(LibCkisSo, 0, data, StrAddr, LibCkisSo.Length);
		int JmpReturnAddr = System.BitConverter.ToInt32(data, InitArrayAddr);
		byte[] byAsmAddr = System.BitConverter.GetBytes(asmAddr);
		Array.ConstrainedCopy(byAsmAddr, 0, data, InitArrayAddr, byAsmAddr.Length);
		byte[] JmpOffset = System.BitConverter.GetBytes(JmpReturnAddr - asmAddr - 0x24);
		Array.ConstrainedCopy(JmpOffset, 0, asm, 0x28, JmpOffset.Length);
		byte[] StrOffset = System.BitConverter.GetBytes(StrAddr - asmAddr - 0x14);
		Array.ConstrainedCopy(StrOffset, 0, asm, 0x24, StrOffset.Length);
		byte[] DlopenOffset = System.BitConverter.GetBytes((dlopenAddr - asmAddr - 0x18) / 4);
		Array.ConstrainedCopy(DlopenOffset, 0, asm, 0x10, StrOffset.Length - 1);
		Array.ConstrainedCopy(asm, 0, data, asmAddr, asm.Length);
		BinaryWriter bw = new BinaryWriter(new FileStream("libc_ok.so", FileMode.Create));
		bw.Write(data);
		bw.Close();
	}
}
           

上面着重讲解了cydia框架的使用以及手动注入jni层的方法,这种方法从原理上,可以100%的过目前的所有的检测,这是单一xposed框架做不到的。