转:http://drops.wooyun.org/tips/7488
官方教程:https://github.com/rovo89/XposedBridge/wiki/Development-tutorial
官网:http://repo.xposed.info/module/de.robv.android.xposed.installer
apk:http://dl-xda.xposed.info/modules/de.robv.android.xposed.installer_v33_36570c.apk
源码:https://github.com/rovo89/XposedInstaller
1.创建工程android4.0.3(api15,测试发现其他版本也可以),可以不用activity
2.修改AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.robv.android.xposed.mods.tutorial" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <meta-data android:name="xposedmodule" android:value="true" /> <meta-data android:name="xposeddescription" android:value="Easy example" /> <meta-data android:name="xposedminversion" android:value="54" /> </application> </manifest>
3.在工程目录下新建一个lib文件夹,将下载好的XposedBridgeApi-54.jar包放入其中.
eclipse 在工程里 选中XposedBridgeApi-54.jar 右键–Build Path–Add to Build Path.
IDEA 鼠标右键点击工程,选择Open Module Settings
,在弹出的窗口中打开Dependencies选项卡.把XposedBridgeApi这个jar包后面的Scope属性改成provided.
4.模块实现接口
package de.robv.android.xposed.mods.tutorial; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; public class Tutorial implements IXposedHookLoadPackage { public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { XposedBridge.log("Loaded app: " + lpparam.packageName); } }
5.入口assets/xposed_init配置,声明需要加载到 XposedInstaller 的入口类:
de.robv.android.xposed.mods.tutorial.Tutorial //完整类名:包名+类名
6.定位要hook的api
7.XposedBridge to hook it
示例如下:
package de.robv.android.xposed.mods.tutorial;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC\_MethodHook; import de.robv.android.xposed.callbacks.XC\_LoadPackage.LoadPackageParam;
public class Tutorial implements IXposedHookLoadPackage { public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { if (!lpparam.packageName.equals("com.android.systemui")) return;
findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
}
});
}
}
IXposedHookLoadPackage
handleLoadPackage : 这个方法用于在加载应用程序的包的时候执行用户的操作
调用示例
public class XposedInterface implements IXposedHookLoadPackage { public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { XposedBridge.log("Kevin-Loaded app: " + lpparam.packageName); } }
参数说明|final LoadPackageParam lpparam 这个参数包含了加载的应用程序的一些基本信息。
XposedHelpers
findAndHookMethod ;这是一个辅助方法,可以通过如下方式静态导入:
importstatic de.robv.android.xposed.XposedHelpers.findAndHookMethod;
使用示例
findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "handleUpdateClock", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { // this will be called before the clock was updated by the original method } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { // this will be called after the clock was updated by the original method } });
findAndHookMethod(Class<?>clazz, //需要Hook的类名 ClassLoader, //类加载器,可以设置为 null String methodName, //需要 Hook 的方法名 Object... parameterTypesAndCallback
该函数的最后一个参数集,包含了:
(1)Hook 的目标方法的参数,譬如:
"com.android.internal.policy.impl.PhoneWindow.DecorView"
是方法的参数的类。
(2)回调方法:
a.XC_MethodHook b.XC_MethodReplacement
1.Dalvik 孵化器 Zygote (Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育/fork出来的)进程对应的程序是/system/bin/app_process. Xposed 框架中真正起作用的是对方法的 hook。
因为 Xposed 工作原理是在/system/bin 目录下替换文件,在 install 的时候需要 root 权限,但是运行时不需要 root 权限。
Log.d(MYTAG+lpparam.packageName, "hello" + lpparam.packageName);
findAndHookMethod("android.app.Application", lpparam.classLoader, "onCreate", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Context context = (Context) param.thisObject; IntentFilter filter = new IntentFilter(myCast.myAction); filter.addAction(myCast.myCmd); context.registerReceiver(new myCast(), filter); } @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { super.afterHookedMethod(param); } });
fristApplication = (Application) param.thisObject;
String appClassName = this.getAppInfo().className;
if (appClassName == null) {
Method hookOncreateMethod = null;
try {
hookOncreateMethod = Application.class.getDeclaredMethod("onCreate", new Class[] {});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
hookhelper.hookMethod(hookOncreateMethod, new ApplicationOnCreateHook());
if(lpparam.appInfo == null || (lpparam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) !=0){ return; }else if(lpparam.isFirstApplication && !ZJDROID_PACKAGENAME.equals(lpparam.packageName)){
Only methods and constructors can be hooked,Cannot hook interfaces,Cannot hook abstract methods 只能 hook 方法和构造方法,不能 hook 接口和抽象方法 抽象类中的非抽象方法是可以 hook的, 接口中的方法不能 hook (接口中的method默认是public abstract 抽象的.field 必须是public static final)
public void myMethod (String a, MyClass b) 通过反射得到自定义类,也可以用[xposedhelpers](https://github.com/rovo89/XposedBridge/wiki/Helpers#class-xposedhelpers)封装好的方法findMethod/findConstructor/callStaticMethod....
Class<?> hookMessageListenerClass = null;
hookMessageListenerClass = lpparam.classLoader.loadClass("org.jivesoftware.smack.MessageListener");
findAndHookMethod("org.jivesoftware.smack.ChatManager", lpparam.classLoader, "createChat", String.class , hookMessageListenerClass ,new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String sendTo = (String) param.args[0];
Log.i(tag , "sendTo : + " + sendTo );
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
例如 java.net.HttpURLConnection extends URLConnection ,
方法在父类
public OutputStream getOutputStream() throws IOException {
throw new UnknownServiceException("protocol doesn't support output");
}
org.apache.http.impl.client.AbstractHttpClient extends CloseableHttpClient ,
public CloseableHttpResponse execute( final HttpHost target, final HttpRequest request, final HttpContext context) throws IOException, ClientProtocolException { return doExecute(target, request, context); } android.async.http复写HttpGet导致zjdroid hook org.apache.http.impl.client.AbstractHttpClient execute 无法获取到请求 url和method
public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) { return findAndHookConstructor(findClass(className, classLoader), parameterTypesAndCallback); }
解1:通过反射到 application 类重写后的 onCreate 方法再对该方法进行hook
解2:hook 构造方法(构造方法被重写,继续解1)
13.native 方法可以 hook,不过是在 java 层调用时hook而不是 hook 动态链接库.
首先确定http 请求的 api,大致分为:
不太了解 java 的 hook 前可以先看下基础的代码HttpClient以及HttpURLConnection的基本使用
对 HttpClient的 hook 可以参考 贾志军大牛的Zjdroid
Method executeRequest = RefInvoke.findMethodExact("org.apache.http.impl.client.AbstractHttpClient", ClassLoader.getSystemClassLoader(),
"execute", HttpHost.class, HttpRequest.class, HttpContext.class);
hookhelper.hookMethod(executeRequest, new AbstractBahaviorHookCallBack() {
@Override
public void descParam(HookParam param) {
// TODO Auto-generated method stub
Logger.log_behavior("Apache Connect to URL ->");
HttpHost host = (HttpHost) param.args[0];
HttpRequest request = (HttpRequest) param.args[1];
if (request instanceof org.apache.http.client.methods.HttpGet) {
org.apache.http.client.methods.HttpGet httpGet = (org.apache.http.client.methods.HttpGet) request;
Logger.log_behavior("HTTP Method : " + httpGet.getMethod());
Logger.log_behavior("HTTP GET URL : " + httpGet.getURI().toString());
Header[] headers = request.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getName());
}
}
} else if (request instanceof HttpPost) {
HttpPost httpPost = (HttpPost) request;
Logger.log_behavior("HTTP Method : " + httpPost.getMethod());
Logger.log_behavior("HTTP URL : " + httpPost.getURI().toString());
Header[] headers = request.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getValue());
}
}
HttpEntity entity = httpPost.getEntity();
String contentType = null;
if (entity.getContentType() != null) {
contentType = entity.getContentType().getValue();
if (URLEncodedUtils.CONTENT_TYPE.equals(contentType)) {
try {
byte[] data = new byte[(int) entity.getContentLength()];
entity.getContent().read(data);
String content = new String(data, HTTP.DEFAULT_CONTENT_CHARSET);
Logger.log_behavior("HTTP POST Content : " + content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (contentType.startsWith(HTTP.DEFAULT_CONTENT_TYPE)) {
try {
byte[] data = new byte[(int) entity.getContentLength()];
entity.getContent().read(data);
String content = new String(data, contentType.substring(contentType.lastIndexOf("=") + 1));
Logger.log_behavior("HTTP POST Content : " + content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}else{
byte[] data = new byte[(int) entity.getContentLength()];
try {
entity.getContent().read(data);
String content = new String(data, HTTP.DEFAULT_CONTENT_CHARSET);
Logger.log_behavior("HTTP POST Content : " + content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
@Override
public void afterHookedMethod(HookParam param) {
// TODO Auto-generated method stub
super.afterHookedMethod(param);
HttpResponse resp = (HttpResponse) param.getResult();
if (resp != null) {
Logger.log_behavior("Status Code = " + resp.getStatusLine().getStatusCode());
Header[] headers = resp.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getValue());
}
}
}
}
});
Method openConnectionMethod = RefInvoke.findMethodExact("java.net.URL", ClassLoader.getSystemClassLoader(), "openConnection"); hookhelper.hookMethod(openConnectionMethod, new AbstractBahaviorHookCallBack() { @Override public void descParam(HookParam param) { // TODO Auto-generated method stub URL url = (URL) param.thisObject; Logger.log_behavior("Connect to URL ->"); Logger.log_behavior("The URL = " + url.toString()); } });
findAndHookMethod("java.io.PrintWriter", lpparam.classLoader, "print",String.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { String print = (String) param.args[0]; Pattern pattern = Pattern.compile("(\\w+=.*)"); Matcher matcher = pattern.matcher(print); if (matcher.matches()) Log.i(tag+lpparam.packageName,"data : " + print); //Log.d(tag,"A :" + print); } });
else {
HttpEntityEnclosingRequestBase httpGet = (HttpEntityEnclosingRequestBase) request;
HttpEntity entity = httpGet.getEntity();
Logger.log_behavior("HttpRequestBase URL : " + httpGet.getURI().toString());
Header[] headers = request.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getName());
}
}
if(entity!= null){
try {
String content = EntityUtils
.toString(entity);
Logger.log_behavior("HTTP entity Content : "
+ content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}