Android. Hook framework xposed
Tutorial: https://github.com/rovo89/XposedBridge/wiki/Development-tutorial
Official Website: 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
Source code: https://github.com/rovo89/XposedInstaller
Basic module development process
1. Create a project android4.0.3 (api15, other versions are also available in the test). You do not need to use the activity.
2. Modify 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. Create a new lib folder under the project directory and put the downloaded XposedBridgeApi-54.jar package into it.
Eclipse right-click the XposedBridgeApi-54.jar in the project-Build Path-Add to Build Path.
Right-click the project and select Open Module Settings. In the displayed window, Open the Dependencies tab. Change the Scope attribute after the jar package XposedBridgeApi to provided.
4. module implementation Interface
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. Entry assets/xposed_init configuration, declare the entry class to be loaded to XposedInstaller:
#! Javade. robv. android. xposed. Mod. tutorial. Tutorial // complete Class Name: package name + class name
6. Locate the api to be hooked
Decompile the target program and view the Smali Code directly in AOSP (android source code ).
7. XposedBridge to hook it
Specify the package name to be hooked to determine whether the currently loaded package is a specified package. specify the method name to be hooked to implement beforeHookedMethod and afterHookedMethod.
Example:
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 } }); } }
Override the two methods beforeHookedMethod and afterHookedMethod of XC_MethodHook. These two methods will be executed before and after the original method. you can use the beforeHookedMethod method to print/tamper with the parameters of the method call (through param. args), or even block calling the original method (send your own results ). the afterHookedMethod method can be used to perform the result based on the original method. you can also use it to manipulate the results. of course, you can add your own code, which will be executed accurately before or after the original method.
Key APIs
IXposedHookLoadPackage
HandleLoadPackage: This method is used to perform user operations when loading the application package.
Call example
public class XposedInterface implements IXposedHookLoadPackage {public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {XposedBridge.log("Kevin-Loaded app: " + lpparam.packageName); }}
Parameter description | final LoadPackageParam lpparam contains some basic information about the loaded application.
XposedHelpers
FindAndHookMethod; this is an auxiliary method, which can be statically imported as follows:
#!javaimport static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
Example
findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "handleUpdateClock", new XC_MethodHook() {@Overrideprotected void beforeHookedMethod(MethodHookParam param) throws Throwable {// this will be called before the clock was updated by the original method }@Overrideprotected void afterHookedMethod(MethodHookParam param) throws Throwable {// this will be called after the clock was updated by the original method }});
Parameter description
FindAndHookMethod (Class <?> Clazz, // ClassLoader of the class to be hooked, // ClassLoader of the Class Loader, which can be set to null String methodName, // name of the method to be hooked Object... parameterTypesAndCallback
The last parameter set of the function, including:
(1) parameters of the target method of the Hook, for example:
"com.android.internal.policy.impl.PhoneWindow.DecorView"
Is the parameter class of the method.
(2) callback method:
a.XC_MethodHook b.XC_MethodReplacement
Details of module development
1. the Dalvik incubator Zygote (in the Android system, all application processes and system service processes SystemServer are generated by the Zygote process/fork) process corresponds to the Program/system/bin/app_process. what really works in the Xposed framework is the method hook.
#! Java, because Xposed works by replacing files in the/system/bin directory, the root permission is required during install, but the root permission is not required during running.
2. Unified log management, tag display package name
#!java Log.d(MYTAG+lpparam.packageName, "hello" + lpparam.packageName);
3. implant a broadcast receiver to dynamically execute commands
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); }});
4. Get context
#!java fristApplication = (Application) param.thisObject;
5. Select the application oncreate program as the injection point to start the function but the onCreate method of MainActivity (this class may be overwritten, so the oncreate method is obtained through reflection)
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());
6. Exclude the system app, exclude itself, and determine the main thread.
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)){
7. hook method
Only methods and constructors can be hooked, Cannot hook interfaces, and Cannot hook abstract methods can Only hook methods and constructor methods, but Cannot hook interfaces and non-abstract methods in abstract classes, methods In the interface cannot be hooked (the method in the interface is public abstract by default. field must be public static final)
8. Custom classes are included in the parameters.
Public void myMethod (String a, MyClass B) Get the custom class through reflection, you can also use the [xposedhelpers] (https://github.com/rovo89/XposedBridge/wiki/Helpers#class-xposedhelpers) encapsulated method findMethod/findConstructor/callStaticMethod ....
9. Reflection of custom classes after injection
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); }});
10. hook the method of a class, which is a subclass and does not overwrite the parent class. In this case, hook the parent class or subclass. (After the hook parent class method, if the subclass is not overwritten, it will take effect. the subclass Rewriting Method requires another hook) (if the subclass overrides the parent class method with spuer, the hook parent class is still valid)
For example, java.net. HttpURLConnection extends URLConnection,
Method in parent class
public OutputStream getOutputStream() throws IOException { throw new UnknownServiceException("protocol doesn't support output"); } org.apache.http.impl.client.AbstractHttpClient extends CloseableHttpClient ,
The method is in the parent class (note that the inherited javasacthttpclient implements org. apache. http. client. HttpClient) of android)
Public CloseableHttpResponse execute (final HttpHost target, final HttpRequest request, final HttpContext context) throws IOException, ClientProtocolException {return doExecute (target, request, context);} android. async. http rewrite HttpGet causes zjdroid hook org. apache. http. impl. client. abstractHttpClient execute cannot obtain the request url and method
11. hook Constructor
public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) { return findAndHookConstructor(findClass(className, classLoader), parameterTypesAndCallback);}
12. 4. The onCreate method of the application is overwritten, such as the Alibaba shell, and rewritten as the native method.
Solution 1: reflect to the onCreate method after the application class is overwritten and then hook the method.
Solution 2: hook Constructor (constructor is overwritten, continue solution 1)
13. The native method can be used to hook, but not to hook the dynamic link library during java-layer calls.
Hook possible HTTP requests in android
First, determine the http request api, which is roughly divided:
HttpClient provided by apache 1) Create HttpClient, GetMethod, PostMethod, HttpRequest, and other objects; 2) set connection parameters; 3) execute HTTP operations; 4) process server return results. httpURLConnection provided by java 1) Create URL and URLConnection/HttpURLConnection object 2) set connection parameters 3) connect to server 4) write data to Server 5) third-party library of webview provided by android: volley/android-async-http/xutils (essentially an extension of the first two methods, method rewriting may affect the subsequent hooks)
Before learning about java hooks, You can first look at the basic code HttpClient and basic use of HttpURLConnection.
For the HttpClient hook, refer to the Zjdroid of Jia Zhijun Daniel
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()); } } } }});
The hook Zjdroid of HttpURLConnection fails to provide a perfect solution. To obtain data fields other than URLs, you must perform operations on the I/O Stream.
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()); }});
The temporary solution I adopted is to perform regular match on I/O, print out the data field similar to the url, and the code is as follows (this code can only solve the preceding HttpUtils and has a false positive, if you have any good ideas, please give me some advice)
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); } });
Zjdroidhook fails because the Android-async-http rewrite of HttpGet (the interpretation of HttpGet and HttpPost is not entered). This problem can be solved by adding an else statement.
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(); } } }
Some common tools Zjdroid: Shelling/api monitoring justTrustMe: Ignore Certificate Validation IntentMonitor: monitor explicit/hidden intent intentXinstaller: Set Application/device properties... XPrivacy: permission management