2 | if (getMid(method) != null)
3 | log("[" + Process.myTid() + "]" + method.getDeclaringClass().getName() + "." + MethodDescription(param).toString() + " ");
4 | else
5 | log("[" + Process.myTid() + "]" + method.getDeclaringClass().getName() + "." + MethodDescription(param).toString() + " ");
6 | log("");
7 | try {
8 | if (args != null) for (int i = 0; i < args.length; i++) {
9 | log("Argument " + i + " ");
10 | log("" + translate(args[i]) + " ");
11 | }
12 |
13 | } catch (Throwable e) {
14 | log("" + e.getLocalizedMessage() + "
");
15 | } finally {
16 | log(" ");
--------------------------------------------------------------------------------
/app/src/main/assets/pages/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/classview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ClassView
5 |
6 |
7 | <#if name?exists>
8 | Current Class:
9 | ${name}
10 | <#if fieldList?exists>
11 | Fields:
12 | <#list fieldList as field>
13 | ${field}
14 | #list>
15 | #if>
16 | <#if methodList?exists>
17 | Methods:
18 | <#list methodList as method>
19 |
20 | #list>
21 | #if>
22 | <#else>
23 | Class Not Found
24 | #if>
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/filemgr.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | File Manager
5 |
6 |
7 | Change Directory
8 |
9 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | XServer - ${pid}
5 |
6 |
7 | Welcome to XServer
8 | Hello, I'm Monkeylord.
9 | XServer is a tool for remote reflect invoking which make all methods accessible from WEB
10 | interface.
11 | It can be used to monitor method calls/returns as well.
12 | Usages
13 | Select a class, then select a method, then begin your monitoring/invoking.
14 | Tips
15 | It can be used to locate/bypass network traffic encryption/sign.
16 | Some Application may have several processes.To monitor specified process, you can use 'adb
17 | forward tcp:8000 tcp:[pid]' instead of 'adb forward tcp:8000 tcp:8000'
18 | Request Infomations:
19 |
20 | Available Operations:
21 | <#list opList as op>
22 |
23 | ${op}
24 |
25 | #list>
26 |
27 | <#if params?exists>
28 |
29 | Your Params:
30 | <#list params?keys as key>
31 | ${key} = ${params[key]}
32 | #list>
33 |
34 | #if>
35 | <#if headers?exists>
36 |
37 | Your Headers:
38 | <#list headers?keys as key>
39 | ${key} = ${headers[key]}
40 | #list>
41 |
42 | #if>
43 | <#if parsers?exists>
44 |
45 | Available Parsers:
46 | <#list parsers?keys as key>
47 | ${key}
48 | #list>
49 |
50 | #if>
51 | <#if objs?exists>
52 |
53 | Captured Objects:
54 | <#list objs?keys as key>
55 | ${key}
56 | #list>
57 |
58 | #if>
59 | <#if objs?exists>
60 |
61 | Known ClassLoaders(click to switch to):
62 | <#list classloaders?keys as key>
63 | ${key}:${classloaders[key]}
64 | #list>
65 |
66 | #if>
67 |
77 | Advanced
78 | Memory Editor
79 | Memory Editor is a basic tool allows you read/write/dump application's memory.
80 | It can be used to extract .dex or .so file in the runtime.
81 | Script Engine
82 | Script Engine is a rhino js engine introduced by Vove7
83 | It allows you script into java like frida. See Rhino Documents for grammar.
84 |
85 | View/Monitor Known Classes:
86 |
92 |
93 |
94 |
95 |
113 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/memory.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MemoryReader
5 |
6 |
7 |
8 | Memory Maps
9 | <#list maps as map>
10 | ${map}
11 | #list>
12 |
13 | Memory Editor:
14 | Address:
15 | Count :
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
52 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/methodview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MethodView
5 |
6 |
7 | Current Class:
8 | ${class}
9 | Current Method:
10 | ${description}
11 | ${javaName}
12 | ${json}
13 | Detail:
14 | <#list parametersTypes as param>
15 | Param:${param.name}
16 | #list>
17 | <#list exceptionTypes as exception>
18 | Throw:${exception.name}
19 | #list>
20 | Return:${returnType.name}
21 |
22 | Invoke
23 | PS: This is for test and is depreciated, You should use MitM tool to intercept/replay invoke.
24 | Each invoke on this method will be rerouted to http://127.0.0.1:8000/invoke2, so MitM tool like burp can intercept that once there's a proxy set.
25 |
48 | Monitor
49 |
50 |
51 |
161 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/objmgr.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/assets/pages/objmgr.html
--------------------------------------------------------------------------------
/app/src/main/assets/pages/script.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Script
5 |
6 |
7 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
Script Content
27 |
45 |
46 | Run
47 | Clear Log
48 | Reconnect
49 |
50 |
51 | app: current application
52 |
53 |
54 | xserver: xserver instance
55 |
56 |
57 | hook: hook provider
58 |
59 |
60 |
61 |
66 |
67 |
68 |
69 |
70 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/app/src/main/assets/pages/tracer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mass Monitor
5 |
7 |
8 |
9 |
10 |
11 | Filters/Controls
12 | Class Filter:
13 | Method Filter(RegEx):
14 | Thread Filter:
15 | Begin Monitoring
16 | Stop Monitoring
17 | Pause
18 |
19 |
20 |
21 |
22 |
23 |
115 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | monkeylord.XServer.XposedEntry
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/MainActivity.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.Context;
6 | import android.content.DialogInterface;
7 | import android.content.SharedPreferences;
8 | import android.content.pm.PackageInfo;
9 | import android.graphics.Color;
10 | import android.os.Bundle;
11 | import android.os.MemoryFile;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.AbsListView;
16 | import android.widget.BaseAdapter;
17 | import android.widget.Button;
18 | import android.widget.EditText;
19 | import android.widget.ImageView;
20 | import android.widget.LinearLayout;
21 | import android.widget.TextView;
22 |
23 | import java.io.BufferedOutputStream;
24 | import java.io.BufferedWriter;
25 | import java.io.File;
26 | import java.io.IOException;
27 | import java.io.OutputStream;
28 | import java.io.OutputStreamWriter;
29 | import java.util.List;
30 |
31 | import monkeylord.XServer.handler.MemoryHandler;
32 |
33 | public class MainActivity extends Activity {
34 | SharedPreferences sp;
35 | String hookee;
36 | boolean isReg;
37 | TextView info;
38 | EditText appname;
39 | //CheckBox regEx;
40 |
41 | private static boolean isModuleActive() {
42 | return false;
43 | }
44 | private static long getSharedMem() {return 0;}
45 |
46 | public void makeWorldReadable(){
47 | new File("/data/data/" + XServer.class.getPackage().getName().toLowerCase()).setExecutable(true, false);
48 | new File("/data/data/" + XServer.class.getPackage().getName().toLowerCase() + "/shared_prefs/XServer.xml").setReadable(true, false);
49 | }
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 | LinearLayout layout = new LinearLayout(this);
55 | LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
56 | ViewGroup.LayoutParams.FILL_PARENT,
57 | ViewGroup.LayoutParams.WRAP_CONTENT);
58 | layout.setOrientation(LinearLayout.VERTICAL);
59 | super.setContentView(layout, param);
60 | sp = getSharedPreferences("XServer", isModuleActive()?MODE_WORLD_READABLE:MODE_PRIVATE);
61 | makeWorldReadable();
62 | hookee = sp.getString("targetApp", "com.");
63 | //isReg = sp.getBoolean("isReg", false);
64 | final AppAdapter appAdapter = new AppAdapter(this);
65 | final AlertDialog selector = new AlertDialog.Builder(this)
66 | .setTitle("Select App")
67 | .setAdapter(appAdapter, new DialogInterface.OnClickListener() {
68 | @Override
69 | public void onClick(DialogInterface dialogInterface, int i) {
70 | hookee = ((PackageInfo) appAdapter.getItem(i)).packageName;
71 | update();
72 | dialogInterface.dismiss();
73 | }
74 | })
75 | .create();
76 | TextView welcome = new TextView(this);
77 | welcome.setText("XServer's App Selector");
78 | welcome.setTextSize(20f);
79 | welcome.setTextColor(Color.BLACK);
80 | info = new TextView(this);
81 | appname = new EditText(this);
82 | //regEx = new CheckBox(this);
83 | //regEx.setText("use RegEx");
84 | Button apply = new Button(this);
85 | apply.setText("Apply");
86 | apply.setOnClickListener(new View.OnClickListener() {
87 | @Override
88 | public void onClick(View view) {
89 | hookee = appname.getText().toString();
90 | //isReg = regEx.isChecked();
91 | update();
92 | }
93 | });
94 | Button selectApp = new Button(this);
95 | selectApp.setText("Select App");
96 | selectApp.setOnClickListener(new View.OnClickListener() {
97 | @Override
98 | public void onClick(View view) {
99 | selector.show();
100 | }
101 | });
102 | TextView tips = new TextView(this);
103 | tips.setText("Note:\n" +
104 | "XServer will listen on 2 ports when target app start: 8000 and PID \n" +
105 | "To connect XServer, ADB command can be used like:\n" +
106 | " adb forward tcp:8000 tcp:8000\n" +
107 | "Or" +
108 | " adb forward tcp:8000 tcp:[PID]\n" +
109 | "Then you can open http://127.0.0.1:8000 in browser to interact with XServer");
110 | tips.setTextSize(15f);
111 | tips.setTextColor(Color.GRAY);
112 | if (!isModuleActive()) {
113 | TextView alert = new TextView(this);
114 | alert.setText("Be Awared: Module Inactive");
115 | alert.setTextColor(Color.RED);
116 | layout.addView(alert);
117 | }
118 | layout.addView(welcome);
119 | layout.addView(info);
120 | layout.addView(appname);
121 | //layout.addView(regEx);
122 | layout.addView(apply);
123 | layout.addView(selectApp);
124 | layout.addView(tips);
125 | update();
126 | }
127 |
128 | public void update() {
129 | SharedPreferences.Editor editor = sp.edit();
130 | editor.putString("targetApp", hookee);
131 | //editor.putBoolean("isReg", isReg);
132 | editor.commit();
133 | info.setText("Target App:\r\n" + hookee);
134 | appname.setText(hookee);
135 | try {
136 | long sharedMem = getSharedMem();
137 | if(sharedMem!=0)MemoryHandler.writeMemory(sharedMem,(hookee+"\0").getBytes());
138 | } catch (Exception e) {
139 | e.printStackTrace();
140 | }
141 | //regEx.setChecked(isReg);
142 | }
143 |
144 | class AppAdapter extends BaseAdapter {
145 | Context context;
146 | List packageInfo;
147 |
148 | AppAdapter(Context context) {
149 | this.context = context;
150 | packageInfo = context.getPackageManager().getInstalledPackages(0);
151 | }
152 |
153 | @Override
154 | public int getCount() {
155 | return packageInfo.size();
156 | }
157 |
158 | @Override
159 | public Object getItem(int i) {
160 | return packageInfo.get(i);
161 | }
162 |
163 | @Override
164 | public long getItemId(int i) {
165 | return 0;
166 | }
167 |
168 | @Override
169 | public View getView(int i, View view, ViewGroup viewGroup) {
170 | //调整了一下应用选择器的外观,感谢@smartdone大佬建议和帮助
171 | //https://github.com/monkeylord/XServer/pull/1/commits/ab718e13a8ef1486f43e1023f62e312b3ff10307
172 | LinearLayout linearLayout = new LinearLayout(context);
173 | linearLayout.setLayoutParams(new AbsListView.LayoutParams(
174 | ViewGroup.LayoutParams.MATCH_PARENT,
175 | ViewGroup.LayoutParams.WRAP_CONTENT));
176 | linearLayout.setOrientation(LinearLayout.HORIZONTAL);
177 | //创建图标
178 | ImageView iv_app_icon = new ImageView(context);
179 | iv_app_icon.setImageDrawable(packageInfo.get(i).applicationInfo.loadIcon(context.getPackageManager()));
180 | iv_app_icon.setLayoutParams(new ViewGroup.LayoutParams(80, 80));
181 | //iv_app_icon.setAdjustViewBounds(true);
182 | iv_app_icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
183 | iv_app_icon.setPadding(5,5,5,5);
184 | linearLayout.addView(iv_app_icon);
185 | //创建文本描述
186 | LinearLayout textLayout=new LinearLayout(context);
187 | textLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
188 | textLayout.setOrientation(LinearLayout.VERTICAL);
189 |
190 | TextView app_display_name = new TextView(context);
191 | app_display_name.setPadding(5, 15, 5, 3);
192 | app_display_name.getPaint().setFakeBoldText(true);
193 | app_display_name.setText(packageInfo.get(i).applicationInfo.loadLabel(context.getPackageManager()));
194 |
195 | TextView app_package_name = new TextView(context);
196 | app_package_name.setPadding(5, 3, 5, 5);
197 | app_package_name.setText( packageInfo.get(i).packageName);
198 |
199 | textLayout.addView(app_display_name);
200 | textLayout.addView(app_package_name);
201 |
202 | linearLayout.addView(textLayout);
203 | Log.e("XServer", "Selector Ready");
204 | return linearLayout;
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/XposedEntry.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.app.AlertDialog;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.pm.ApplicationInfo;
9 | import android.content.res.XModuleResources;
10 | import android.os.Build;
11 | import android.os.Process;
12 | import android.system.OsConstants;
13 | import android.util.Log;
14 |
15 | import com.blankj.utilcode.util.NetworkUtils;
16 |
17 | import java.io.File;
18 | import java.io.FileDescriptor;
19 | import java.io.RandomAccessFile;
20 | import java.lang.reflect.InvocationTargetException;
21 | import java.lang.reflect.Member;
22 | import java.util.HashMap;
23 |
24 | import de.robv.android.xposed.IXposedHookLoadPackage;
25 | import de.robv.android.xposed.IXposedHookZygoteInit;
26 | import de.robv.android.xposed.XC_MethodHook;
27 | import de.robv.android.xposed.XC_MethodReplacement;
28 | import de.robv.android.xposed.XSharedPreferences;
29 | import de.robv.android.xposed.XposedBridge;
30 | import de.robv.android.xposed.XposedHelpers;
31 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
32 | import monkeylord.XServer.handler.Hook.Unhook;
33 | import monkeylord.XServer.handler.Hook.XServer_MethodHook;
34 | import monkeylord.XServer.handler.Hook.XServer_Param;
35 | import monkeylord.XServer.handler.HookHandler;
36 | import monkeylord.XServer.handler.MemoryHandler;
37 |
38 | import static monkeylord.XServer.utils.Utils.getMyIp;
39 |
40 | /*
41 | 某些Android 4版本,需要修改依赖库的配置才能兼容,否则会报pre-verifed错误。
42 | 原因:Framework也提供了XposedBridgeApi,和编译进插件的内容重复。所以要把XposedBridgeApi从编译改为引用。
43 | 修改:Build->Edit Libraries and Dependencies 将XposedBridgeApi的scope从compile改为provided
44 |
45 | */
46 |
47 | public class XposedEntry implements IXposedHookLoadPackage, IXposedHookZygoteInit {
48 |
49 | public static boolean debug=true;
50 | public static ClassLoader classLoader;
51 | public static XModuleResources res;
52 | public static XSharedPreferences sPrefs;
53 | String packageName;
54 | Boolean isFirstApplication;
55 | String processName;
56 | ApplicationInfo appInfo;
57 | long smAddr;
58 |
59 | @Override
60 | public void initZygote(StartupParam startupParam) throws Throwable {
61 | res = XModuleResources.createInstance(startupParam.modulePath, null);
62 | sPrefs = new XSharedPreferences(this.getClass().getPackage().getName().toLowerCase(), "XServer");
63 | sPrefs.makeWorldReadable();
64 | try{
65 | String targetApp = sPrefs.getString("targetApp", "MadMode");
66 | File file = new File("/dev/zero");
67 | RandomAccessFile randomAccessFile = new RandomAccessFile(file,"rw");
68 | FileDescriptor fd = randomAccessFile.getFD();
69 | if(!fd.valid())smAddr = 0;
70 | else{
71 | try {
72 | smAddr = MemoryHandler.mmap(0, 1024, OsConstants.PROT_READ | OsConstants.PROT_WRITE, OsConstants.MAP_SHARED, fd, 0);
73 | MemoryHandler.writeMemory(smAddr,(targetApp+"\0").getBytes());
74 | }catch (InvocationTargetException e){
75 | throw e.getTargetException();
76 | }finally {
77 | randomAccessFile.close();
78 | }
79 | }
80 | }catch (Exception e){
81 | Log.e("[XServer Experiment]", e.getMessage()+e.toString());
82 | }
83 | }
84 |
85 | @SuppressLint("MissingPermission")
86 | @Override
87 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
88 |
89 | //告知界面模块已启动,同时解除Android N以上对MODE_WORLD_READABLE的限制
90 | if (loadPackageParam.packageName.equals("monkeylord.xserver")) {
91 | XposedHelpers.findAndHookMethod("monkeylord.XServer.MainActivity", loadPackageParam.classLoader, "isModuleActive", XC_MethodReplacement.returnConstant(true));
92 | XposedHelpers.findAndHookMethod("monkeylord.XServer.MainActivity", loadPackageParam.classLoader, "getSharedMem", XC_MethodReplacement.returnConstant(smAddr));
93 | if (Build.VERSION.SDK_INT >= 24)XposedHelpers.findAndHookMethod("android.app.ContextImpl", loadPackageParam.classLoader, "checkMode",int.class, XC_MethodReplacement.returnConstant(null));
94 | XposedBridge.log("XServer handleLoadPackage: "+ Build.VERSION.SDK_INT);
95 | }
96 | //获取目标包名
97 | sPrefs.reload();
98 | String targetApp = sPrefs.getString("targetApp", "MadMode");
99 | if (targetApp.equals("MadMode")&&smAddr!=0)targetApp = new String(MemoryHandler.readMemory(smAddr,1024)).split("\0")[0];
100 | //if(targetApp.equals("MadMode"))XposedBridge.log("XServer Cannot Figure Out TargetApp...Hooking Everyone Now!!");
101 | if (!targetApp.equals("MadMode")&&!loadPackageParam.packageName.equals(targetApp)) return;
102 | gatherInfo(loadPackageParam);
103 | //启动XServer
104 | setXposedHookProvider();
105 | if(!targetApp.equals("MadMode"))new XServer(8000);
106 | new XServer(Process.myPid());
107 | final String ip = getMyIp();
108 | XposedBridge.log("XServer Listening... " + loadPackageParam.packageName + " --> http://" + ip + ":" + Process.myPid());
109 | XposedBridge.log("Using XposedHook...@" + Process.myPid());
110 | if(!targetApp.equals("MadMode"))XposedHelpers.findAndHookMethod(Activity.class, "onStart", new XC_MethodHook() {
111 | @Override
112 | public void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
113 | new AlertDialog.Builder((Context) param.thisObject)
114 | .setTitle("XServer activated")
115 | .setMessage("Open:http://" + ip + ":" + Process.myPid())
116 | .setPositiveButton("ok", new DialogInterface.OnClickListener() {
117 | @Override
118 | public void onClick(DialogInterface dialogInterface, int i) {
119 | dialogInterface.dismiss();
120 | }
121 | })
122 | .create().show();
123 | XposedBridge.unhookMethod(param.method,this);
124 | }
125 | });
126 |
127 | }
128 |
129 | void setXposedHookProvider(){
130 | XServer.classLoader = classLoader;
131 | XServer.assetManager = res.getAssets();
132 | HookHandler.setProvider(new HookHandler.HookProvider() {
133 | // 复用Hook,否则在大量Hook时会OOM
134 | HashMap pairs = new HashMap<>();
135 | @Override
136 | public Unhook hookMethod(Member hookMethod, final XServer_MethodHook mycallback) {
137 | XC_MethodHook myCallback = pairs.get(mycallback);
138 | if(myCallback==null)myCallback = new XC_MethodHook() {
139 | XServer_MethodHook callback = mycallback;
140 | @Override
141 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
142 | super.beforeHookedMethod(param);
143 | XServer_Param xsParam = new XServer_Param();
144 | xsParam.args = param.args==null ? new Object[0] : param.args;
145 | xsParam.method = param.method;
146 | xsParam.thisObject = param.thisObject;
147 | xsParam.throwable = param.getThrowable();
148 | xsParam.result = param.getResult();
149 | callback.beforeHookedMethod(xsParam);
150 | if (xsParam.returnEarly) {
151 | if (xsParam.hasThrowable()) param.setThrowable(xsParam.getThrowable());
152 | else param.setResult(xsParam.result);
153 | }
154 | }
155 |
156 | @Override
157 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
158 | super.afterHookedMethod(param);
159 | XServer_Param xsParam = new XServer_Param();
160 | xsParam.args = param.args;
161 | xsParam.method = param.method;
162 | xsParam.thisObject = param.thisObject;
163 | xsParam.throwable = param.getThrowable();
164 | xsParam.result = param.getResult();
165 | callback.afterHookedMethod(xsParam);
166 | if (xsParam.hasThrowable()) param.setThrowable(xsParam.getThrowable());
167 | else param.setResult(xsParam.result);
168 | }
169 | };
170 | pairs.put(mycallback,myCallback);
171 | Object unhook = XposedBridge.hookMethod(hookMethod, myCallback);
172 | return new Unhook(hookMethod, unhook);
173 | }
174 |
175 | @Override
176 | public void unhookMethod(Member hookMethod, Object additionalObj) {
177 | if(additionalObj!=null)((XC_MethodHook.Unhook)additionalObj).unhook();
178 | }
179 | });
180 | }
181 |
182 | private void gatherInfo(XC_LoadPackage.LoadPackageParam loadPackageParam) {
183 | packageName = loadPackageParam.packageName;
184 | isFirstApplication = loadPackageParam.isFirstApplication;
185 | classLoader = loadPackageParam.classLoader;
186 | processName = loadPackageParam.processName;
187 | appInfo = loadPackageParam.appInfo;
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/BaseOperation.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import monkeylord.XServer.XServer;
7 | import monkeylord.XServer.utils.NanoHTTPD;
8 |
9 | import static monkeylord.XServer.utils.NanoHTTPD.newFixedLengthResponse;
10 |
11 | public abstract class BaseOperation implements XServer.Operation {
12 | public NanoHTTPD.Response handle(NanoHTTPD.IHTTPSession session){
13 | Map files = new HashMap();
14 | Map headers = null;
15 | try {
16 | headers = session.getHeaders();
17 | session.parseBody(files);
18 | } catch (Exception e) {
19 | e.printStackTrace();
20 | }
21 | String uri = session.getUri();
22 | return newFixedLengthResponse(handle(uri, session.getParms(), headers, files));
23 | }
24 |
25 | abstract String handle(String url, Map parms, Map headers, Map files);
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/ClassView.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 |
5 | import java.util.Map;
6 |
7 | import monkeylord.XServer.XServer;
8 | import monkeylord.XServer.handler.ClassHandler;
9 | import monkeylord.XServer.utils.DexHelper;
10 |
11 | //类详情查看页面
12 | public class ClassView extends BaseOperation {
13 | @Override
14 | public String handle(String url, Map parms, Map headers, Map files) {
15 | try {
16 | if(parms.get("op")!=null) {
17 | JSONObject res = new JSONObject();
18 | switch (parms.get("op")){
19 | case "getclass":
20 | res.put("class",
21 | ClassHandler.getClassDetail(ClassHandler.findClassbyName(parms.get("class"), XServer.classLoader)));
22 | return res.toJSONString();
23 | case "getclasses":
24 | res.put("classes",
25 | DexHelper.getClassesInDex(XServer.classLoader));
26 | return res.toJSONString();
27 | case "setclassloader":
28 | res.put("success",ClassHandler.setXServerClassloader(parms.get("classloader")));
29 | return " ";
30 | default:
31 | return "";
32 | }
33 | }else{
34 | return XServer.render(
35 | ClassHandler.getClassDetail(
36 | ClassHandler.findClassbyName(parms.get("class"), XServer.classLoader)),
37 | "pages/classview.html");
38 | }
39 | } catch (Exception e) {
40 | return e.getLocalizedMessage();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/FileAccess.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.JSONObject;
5 |
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.FileWriter;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 |
12 | import monkeylord.XServer.XServer;
13 | import monkeylord.XServer.utils.NanoHTTPD;
14 |
15 | import static monkeylord.XServer.utils.NanoHTTPD.newChunkedResponse;
16 | import static monkeylord.XServer.utils.NanoHTTPD.newFixedLengthResponse;
17 |
18 | public class FileAccess implements XServer.Operation {
19 | @Override
20 | public NanoHTTPD.Response handle(NanoHTTPD.IHTTPSession session) {
21 | Map files = new HashMap();
22 | try {
23 | session.parseBody(files);
24 | } catch (Exception e) {
25 | e.printStackTrace();
26 | }
27 | if(session.getParms().get("op")!=null) {
28 | try {
29 | File file = new File(session.getParms().get("path"));
30 | switch (session.getParms().get("op")) {
31 | case "mkdir":
32 | if(file.mkdirs())return newFixedLengthResponse("Done");
33 | else throw new Exception("Fail to mkdir.");
34 | case "readdir":
35 | HashMap fileList = new HashMap<>();
36 | for (File listfile:file.listFiles()) {
37 | JSONObject fileObj = new JSONObject();
38 | fileObj.put("isDirectory",listfile.isDirectory());
39 | fileObj.put("lastModified",listfile.lastModified());
40 | fileObj.put("length",listfile.length());
41 | fileObj.put("canRead",listfile.canRead());
42 | fileObj.put("canWrite",listfile.canWrite());
43 | fileObj.put("canExecute",listfile.canExecute());
44 | fileList.put(listfile.getName(),fileObj);
45 | }
46 | if(fileList==null)throw new Exception("Cannot access directory");
47 | return newFixedLengthResponse(JSON.toJSONString(fileList));
48 | case "read":
49 | //if(!file.canRead())file.setReadable(true);
50 | if(!file.canRead())throw new Exception("Cannot access file");
51 | FileInputStream fis = new FileInputStream(file);
52 | NanoHTTPD.Response res = newChunkedResponse(NanoHTTPD.Response.Status.OK,"application/octet–stream",fis);
53 | res.addHeader("Content-Disposition","attachment;filename="+file.getName());
54 | return res;
55 | case "write":
56 | FileWriter fw = new FileWriter(file);
57 | fw.write(files.get("postData"));
58 | fw.close();
59 | return newFixedLengthResponse("Done");
60 | case "delete":
61 | if(file.delete())return newFixedLengthResponse("Done");
62 | else throw new Exception("Fail to delete.");
63 | default:
64 | return newFixedLengthResponse("Unknown Operation");
65 | }
66 | }catch (Exception e){
67 | return newFixedLengthResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_HTML, e.getLocalizedMessage());
68 | }
69 | }else{
70 | HashMap map = new HashMap<>();
71 | String appName = XServer.currentApp;
72 | map.put("dir", (appName!=null)?"/data/data/"+ appName: "/");
73 | try {
74 | return newFixedLengthResponse(XServer.render(map, "pages/filemgr.html"));
75 | } catch (Exception e) {
76 | return newFixedLengthResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_HTML, e.getLocalizedMessage());
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/Invoke.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.Map;
5 |
6 | import monkeylord.XServer.XServer;
7 | import monkeylord.XServer.handler.ObjectHandler;
8 |
9 | //处理反射调用
10 | public class Invoke extends BaseOperation {
11 | @Override
12 | public String handle(String url, Map parms, Map headers, Map files) {
13 | StringBuilder sb = new StringBuilder();
14 | Object thisobj = null;
15 | Object[] params;
16 | if ("app".equalsIgnoreCase(parms.get("thisobj"))) {
17 | thisobj = XServer.currentApplication;
18 | } else {
19 | thisobj = parms.get("thisobj").equals("null") ? null : ObjectHandler.objects.get(parms.get("thisobj"));
20 | }
21 |
22 | try {
23 | Method[] methods = Class.forName(parms.get("class"), false, XServer.classLoader).getDeclaredMethods();
24 | Method m = methods[Integer.parseInt(parms.get("method"))];
25 | params = new Object[m.getParameterTypes().length];
26 | for (int i = 0; i < m.getParameterTypes().length; i++) {
27 | params[i] = XServer.parsers.get(parms.get("parser" + i)).parse(parms.get("param" + i));
28 | }
29 | m.setAccessible(true);
30 | sb.append(m.invoke(thisobj, params));
31 | } catch (Exception e) {
32 | sb.append(e.getLocalizedMessage());
33 | }
34 | return sb.toString();
35 | }
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/Invoke_New.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import android.util.Log;
4 |
5 | import com.alibaba.fastjson.JSON;
6 | import com.alibaba.fastjson.JSONArray;
7 | import com.alibaba.fastjson.JSONObject;
8 |
9 | import java.lang.reflect.Method;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | import monkeylord.XServer.XServer;
14 | import monkeylord.XServer.handler.MethodHandler;
15 | import monkeylord.XServer.handler.ObjectHandler;
16 | import monkeylord.XServer.utils.NanoHTTPD;
17 |
18 | //处理反射调用的另一个接口
19 | public class Invoke_New implements XServer.Operation {
20 | @Override
21 | public NanoHTTPD.Response handle(NanoHTTPD.IHTTPSession session){
22 | Map files = new HashMap();
23 | Map headers = null;
24 | try {
25 | headers = session.getHeaders();
26 | session.parseBody(files);
27 | } catch (Exception e) {
28 | e.printStackTrace();
29 | }
30 | String uri = session.getUri();
31 | StringBuilder sb = new StringBuilder();
32 | try {
33 | Log.d("XServer", "Invoke2 postData:"+files.get("postData"));
34 | JSONObject data = JSON.parseObject(files.get("postData"));
35 | Method method= MethodHandler.getMethodbyJavaName(data.getString("method"));
36 | //method.setAccessible(true);
37 | Object thisobj = (data.getString("this")!=null)?ObjectHandler.parseObject(data.getString("this")):null;
38 | Object[] params = new Object[method.getParameterTypes().length];
39 | JSONArray paramList = data.getJSONArray("params");
40 | for (int i = 0; i < params.length; i++) {
41 | params[i]=ObjectHandler.parseObject(paramList.getString(i));
42 | Log.d("XServer", "Invoke2 Arg"+i+":"+paramList.getString(i));
43 | }
44 | method.setAccessible(true);
45 | sb.append(ObjectHandler.saveObject(method.invoke(thisobj, params)));
46 | } catch (Exception e) {
47 | sb.append(e.getMessage());
48 | sb.append("\r\n");
49 | for (StackTraceElement st:e.getStackTrace()) {
50 | sb.append(st.toString());
51 | sb.append('\n');
52 | }
53 | e.printStackTrace();
54 | Log.i("[XServer Debug]", sb.toString() );
55 | return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR,NanoHTTPD.MIME_PLAINTEXT,ObjectHandler.saveObject(new XServerWrappedThrowable(e.getCause(),true)));
56 | }
57 | Log.d("XServer", "Invoke2 return:"+sb.toString());
58 | return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.OK,NanoHTTPD.MIME_PLAINTEXT,sb.toString());
59 | }
60 | public static boolean isMe(){
61 | StackTraceElement[] stacks = Thread.currentThread().getStackTrace();
62 | for (int i = 4; i parms, Map headers, Map files) {
21 | try {
22 | Method method=null;
23 | if(parms.get("op")!=null) {
24 | switch (parms.get("op")){
25 | case "maps":
26 | JSONObject res = new JSONObject();
27 | res.put("maps",MemoryHandler.getMaps());
28 | return res.toJSONString();
29 | case "dump":
30 | byte[] binary=MemoryHandler.readMemory(Long.parseLong(parms.get("addr")),Integer.parseInt(parms.get("count")));
31 | FileOutputStream fo = new FileOutputStream(new File("/sdcard/XServer.dump"));
32 | fo.write(binary);
33 | return "Dumped at /sdcard/XServer.dump";
34 | case "read":
35 | byte[] bytes=MemoryHandler.readMemory(Long.parseLong(parms.get("addr")),Integer.parseInt(parms.get("count")));
36 | return new String(encodeHex(bytes));
37 | case "write":
38 | MemoryHandler.writeMemory(Long.parseLong(parms.get("addr")),hexStringToBytes(files.get("postData")));
39 | byte[] bytes_res=MemoryHandler.readMemory(Long.parseLong(parms.get("addr")),hexStringToBytes(files.get("postData")).length);
40 | return new String(encodeHex(bytes_res));
41 | default:
42 | return "";
43 | }
44 | }else{
45 | HashMap map = new HashMap<>();
46 | map.put("maps", MemoryHandler.getMaps());
47 | return XServer.render(map, "pages/memory.html");
48 | }
49 | } catch (Exception e) {
50 | return e.getLocalizedMessage();
51 | }
52 | }
53 |
54 | public static String bytesToHexString(byte[] src){
55 | StringBuffer stringBuffer = new StringBuffer(src.length*2+2);
56 | byte[] hexs = "0123456789ABCDEF".getBytes();
57 | if (src == null || src.length <= 0) {
58 | return null;
59 | }
60 | for (int i = 0; i < src.length; i++) {
61 | //Log.e("XServer", "bytesToHexString: "+((src[i]&0xFF)>>4));
62 | //Log.e("XServer", "bytesToHexString: "+((src[i]&0x0F)));
63 | stringBuffer.append((char) (hexs[(src[i]&0xFF)>>4]));
64 | stringBuffer.append((char) (hexs[src[i]&0x0F]));
65 | /*
66 | int v = src[i] & 0xFF;
67 | String hv = Integer.toHexString(v);
68 | if (hv.length() < 2) {
69 | stringBuffer.append(0);
70 | }
71 | stringBuffer.append(hv);
72 | */
73 | }
74 | return stringBuffer.toString();
75 | }
76 |
77 | private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
78 | public static char[] encodeHex(byte[] data) {
79 | int l = data.length;
80 | char[] out = new char[l << 1];
81 | int i = 0;
82 | for (int j = 0; i < l; i++) {
83 | out[(j++)] = DIGITS[((0xF0 & data[i]) >>> 4)];
84 | out[(j++)] = DIGITS[(0xF & data[i])];
85 | }
86 | return out;
87 | }
88 |
89 | public static byte[] hexStringToBytes(String hexString) {
90 | if (hexString == null || hexString.equals("")) {
91 | return null;
92 | }
93 | hexString = hexString.toUpperCase();
94 | int length = hexString.length() / 2;
95 | char[] hexChars = hexString.toCharArray();
96 | byte[] d = new byte[length];
97 | for (int i = 0; i < length; i++) {
98 | int pos = i * 2;
99 | d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
100 | }
101 | return d;
102 | }
103 | /**
104 | * Convert char to byte
105 | * @param c char
106 | * @return byte
107 | */
108 | private static byte charToByte(char c) {
109 | return (byte) "0123456789ABCDEF".indexOf(c);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/MethodView.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import monkeylord.XServer.XServer;
8 | import monkeylord.XServer.handler.ClassHandler;
9 | import monkeylord.XServer.handler.MethodHandler;
10 | import monkeylord.XServer.handler.ObjectHandler;
11 | import monkeylord.XServer.utils.Utils;
12 |
13 | //查看方法详情页面
14 | public class MethodView extends BaseOperation {
15 | @Override
16 | public String handle(String url, Map parms, Map headers, Map files) {
17 | try {
18 | Method method=null;
19 | if(parms.get("javaname")!=null) {
20 | method = MethodHandler.getMethodbyJavaName(parms.get("javaname"));
21 | for (int i = 0; i < method.getDeclaringClass().getDeclaredMethods().length; i++) {
22 | if(Utils.getJavaName(method.getDeclaringClass().getDeclaredMethods()[i]).equals(parms.get("javaname"))){
23 | parms.put("method",""+i);
24 | }
25 | }
26 | }
27 | if(method==null)method=ClassHandler.findClassbyName(parms.get("class"),XServer.classLoader).getDeclaredMethods()[Integer.parseInt(parms.get("method"))];
28 | HashMap map = MethodHandler.getMethodDetail(method);
29 | map.put("method",parms.get("method"));
30 | map.put("objList", ObjectHandler.objects.keySet());
31 | return XServer.render(map, "pages/methodview.html");
32 | } catch (Exception e) {
33 | return e.getLocalizedMessage();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/ObjManager.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import monkeylord.XServer.XServer;
7 |
8 | //对象管理器页面
9 | //TODO 增删改查编辑暂存的对象
10 | public class ObjManager extends BaseOperation {
11 | @Override
12 | public String handle(String url, Map parms, Map headers, Map files) {
13 | try {
14 | Map map = new HashMap();
15 | if (parms.containsKey("type")) {
16 | switch (parms.get("type")) {
17 | case "show":
18 | break;
19 | case "edit":
20 | break;
21 | default:
22 | return "";
23 | }
24 | } else return XServer.render(map, "pages/objmgr.html");
25 | return null;
26 | } catch (Exception e) {
27 | return e.getLocalizedMessage();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/Script.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import monkeylord.XServer.XServer;
7 |
8 | /**
9 | * Created by Vove on 2020/11/22
10 | */
11 | public class Script extends BaseOperation {
12 | @Override
13 | String handle(String url, Map parms, Map headers, Map files) {
14 | try {
15 | Map map = new HashMap();
16 | map.put("filter", parms.get("filter"));
17 | return XServer.render(map, "pages/script.html");
18 | } catch (Exception e) {
19 | return e.getLocalizedMessage();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/Tracer.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import monkeylord.XServer.XServer;
7 |
8 | //MassTracer页面
9 | public class Tracer extends BaseOperation {
10 | @Override
11 | public String handle(String url, Map parms, Map headers, Map files) {
12 | try {
13 | Map map = new HashMap();
14 | map.put("filter", parms.get("filter"));
15 | return XServer.render(map, "pages/tracer2.html");
16 | } catch (Exception e) {
17 | return e.getLocalizedMessage();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/WsScript.kt:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api
2 |
3 | import android.os.Handler
4 | import android.os.HandlerThread
5 | import cn.vove7.rhino.RhinoHelper
6 | import cn.vove7.rhino.api.RhinoApi
7 | import monkeylord.XServer.XServer
8 | import monkeylord.XServer.XServer.wsOperation
9 | import monkeylord.XServer.handler.HookHandler
10 | import monkeylord.XServer.utils.NanoHTTPD.IHTTPSession
11 | import monkeylord.XServer.utils.NanoWSD.WebSocket
12 | import monkeylord.XServer.utils.NanoWSD.WebSocketFrame
13 | import monkeylord.XServer.utils.NanoWSD.WebSocketFrame.CloseCode
14 | import java.io.IOException
15 | import java.text.SimpleDateFormat
16 | import java.util.*
17 |
18 | /**
19 | * # WsScript
20 | *
21 | * @author liben
22 | * 2020/11/22
23 | */
24 | class WsScript : wsOperation {
25 | companion object {
26 | @JvmStatic
27 | val PATH = "/wsScript"
28 |
29 | val engineThread by lazy {
30 | HandlerThread("WsScript").also {
31 | it.start()
32 | }
33 | }
34 | val engineHandler by lazy {
35 | Handler(engineThread.looper)
36 | }
37 |
38 | private val engine by lazy {
39 | RhinoHelper(
40 | XServer.getCurrentApplication(),
41 | mapOf("app" to XServer.getCurrentApplication()
42 | ,"xserver" to XServer.instance
43 | ,"hook" to HookHandler.getProvider())
44 | )
45 | }
46 | }
47 |
48 | private var ws: WebSocketHandler? = null
49 | private val sf = SimpleDateFormat("HH:mm:ss:SSS", Locale.getDefault())
50 |
51 | val onPrint: RhinoApi.OnPrint = RhinoApi.OnPrint { l, s ->
52 | ws?.trySend("[$l] $s")
53 | }
54 |
55 |
56 | override fun handle(handshake: IHTTPSession?): WebSocket {
57 | ws = WebSocketHandler(handshake);
58 | return ws!!
59 | }
60 |
61 | fun handleMessage(data: String?) = engineHandler.post {
62 | kotlin.runCatching {
63 | engine.evalString(data ?: "", null as Array<*>?)
64 | }.onFailure {
65 | it.printStackTrace()
66 | ws?.trySend(it.toString())
67 | }
68 | }
69 |
70 | private inner class WebSocketHandler(
71 | handshakeRequest: IHTTPSession?
72 | ) : WebSocket(handshakeRequest) {
73 | fun trySend(payload: String?) {
74 | try {
75 | send(sf.format(Date()) + ": " + payload)
76 | } catch (e: IOException) {
77 | e.printStackTrace()
78 | }
79 | }
80 |
81 | override fun onMessage(message: WebSocketFrame) {
82 | handleMessage(message.textPayload)
83 | }
84 |
85 | override fun onOpen() {
86 | RhinoApi.regPrint(onPrint)
87 | trySend("Connect Success...")
88 | }
89 |
90 | override fun onClose(code: CloseCode, reason: String, initiatedByRemote: Boolean) {
91 | RhinoApi.unregPrint(onPrint)
92 | engine.release()
93 | }
94 |
95 | override fun onPong(pong: WebSocketFrame) {
96 |
97 | }
98 |
99 | override fun onException(exception: IOException) {
100 | exception.printStackTrace()
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/wsMethodViewNew.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import android.os.Process;
4 | import android.util.Log;
5 |
6 | import com.alibaba.fastjson.JSONArray;
7 | import com.alibaba.fastjson.JSONObject;
8 |
9 | import java.io.IOException;
10 | import java.lang.reflect.InvocationTargetException;
11 | import java.lang.reflect.Member;
12 | import java.lang.reflect.Method;
13 | import java.util.ArrayList;
14 | import java.util.HashMap;
15 |
16 | import monkeylord.XServer.XServer;
17 | import monkeylord.XServer.handler.Hook.Unhook;
18 | import monkeylord.XServer.handler.Hook.XServer_MethodHook;
19 | import monkeylord.XServer.handler.Hook.XServer_Param;
20 | import monkeylord.XServer.handler.HookHandler;
21 | import monkeylord.XServer.handler.MethodHandler;
22 | import monkeylord.XServer.handler.ObjectHandler;
23 | import monkeylord.XServer.utils.NanoHTTPD;
24 | import monkeylord.XServer.utils.NanoWSD;
25 | import monkeylord.XServer.utils.Utils;
26 | import monkeylord.XServer.utils.netUtil;
27 |
28 | //单方法监控的WebSocket处理
29 | public class wsMethodViewNew implements XServer.wsOperation {
30 |
31 | final boolean unhookOnClose = true;
32 | //HashMap unhooks = new HashMap<>();
33 | HashMap wss = new HashMap<>();
34 | HashMap methods = new HashMap<>();
35 | //ws websocket;
36 | //MethodHook hook;
37 | //public Method m = null;
38 | public String server="http://127.0.0.1:8000";//TODO +Process.myPid();
39 |
40 | @Override
41 | public NanoWSD.WebSocket handle(NanoHTTPD.IHTTPSession handshake) {
42 | return new ws(handshake);
43 | }
44 |
45 | public Object wsInvoke(Object[] params) throws InvocationTargetException, IllegalAccessException {
46 | return methods.get(this).invoke(params);
47 | }
48 |
49 | public class ws extends NanoWSD.WebSocket {
50 |
51 | Unhook unhook = null;
52 | boolean modify = true;
53 | HashMap objs = new HashMap<>();
54 |
55 | public ws(NanoHTTPD.IHTTPSession handshakeRequest) {
56 | super(handshakeRequest);
57 | Method m = MethodHandler.getMethodbyJavaName((handshakeRequest.getParms().get("javaname")));
58 | methods.put(this,m);
59 | }
60 |
61 | public void trySend(String payload) {try {send(payload);} catch (IOException e) {e.printStackTrace();}}
62 |
63 | @Override
64 | protected void onOpen() {
65 | Method m = methods.get(this);
66 | MethodHook hook = new MethodHook(m);
67 | wss.put(hook,this);
68 | JSONObject res = new JSONObject();
69 | res.put("type","hook");
70 | res.put("method", Utils.getJavaName(m));
71 | res.put("ishooked", unhook!=null);
72 | res.put("relatedObjects",new JSONArray());
73 | trySend(res.toJSONString());
74 | }
75 |
76 | @Override
77 | protected void onClose(NanoWSD.WebSocketFrame.CloseCode code, String reason, boolean initiatedByRemote) {
78 | // TODO: should unhook on close
79 | }
80 | @Override
81 | protected void onMessage(NanoWSD.WebSocketFrame message) {}
82 | @Override
83 | protected void onPong(NanoWSD.WebSocketFrame pong) {}
84 | @Override
85 | protected void onException(IOException exception) {}
86 | }
87 |
88 | public class MethodHook extends XServer_MethodHook {
89 | public Member method; //被Hook的方法
90 | public Object thisObject; //方法被调用时的this对象
91 | public Object[] args; //方法被调用时的参数
92 | private Object result = null; //方法被调用后的返回结果
93 | private int tid = 0;
94 |
95 | MethodHook(Method method) { HookHandler.getProvider().hookMethod(method, this); }
96 |
97 | public void setTid(int tid) {
98 | this.tid = tid;
99 | }
100 |
101 | private void gatherInfo(XServer_Param param) {
102 | method = param.method;
103 | thisObject = param.thisObject;
104 | args = param.args;
105 | }
106 |
107 | public void beforeHookedMethod(XServer_Param param) throws Throwable {
108 | super.beforeHookedMethod(param);
109 | if (tid > 0 && tid != Process.myPid()) return;
110 | gatherInfo(param);
111 |
112 | // 如果是自己调用的路过的信息,就忽略
113 | if(Invoke_New.isMe())return;
114 |
115 | // 先把信息通过WebSocket发送出去
116 | // 详细信息去ObjectManager查
117 | Method m = methods.get(wss.get(this));
118 | JSONObject res = new JSONObject();
119 | JSONArray params = new JSONArray();
120 | for (Object arg:param.args) { params.add(ObjectHandler.saveObject(arg)); }
121 | res.put("type","before");
122 | res.put("method", Utils.getJavaName(m));
123 | res.put("this", ObjectHandler.saveObject(param.thisObject));
124 | res.put("params",params);
125 | res.put("stack", Thread.currentThread().getStackTrace());
126 | res.put("isproxy",Invoke_New.isMe());
127 | res.put("threadid", Process.myTid());
128 | wss.get(this).trySend(res.toJSONString());
129 |
130 | if (wss.get(this).modify) {
131 | JSONObject call = new JSONObject();
132 |
133 | call.put("method", Utils.getJavaName(m));
134 | if(thisObject!=null)call.put("this",ObjectHandler.saveObject(thisObject));
135 | call.put("params",params);
136 | ArrayList stacks=new ArrayList();
137 | for (StackTraceElement element:Thread.currentThread().getStackTrace()) {
138 | stacks.add(element.getClassName() + "." + element.getMethodName() + " : " + element.getLineNumber());
139 | }
140 | call.put("stackTrace",stacks);
141 | Object result = ObjectHandler.parseObject(new netUtil(server + "/invoke2", new org.json.JSONObject(call.toJSONString()).toString(2)).getRet());
142 | Log.e("[XServer Debug]", "result: " + result);
143 | if(result instanceof Invoke_New.XServerWrappedThrowable){
144 | if(((Invoke_New.XServerWrappedThrowable)result).shouldPassthough)
145 | param.setThrowable(((Invoke_New.XServerWrappedThrowable)result).getThrowable());
146 | }
147 | else param.setResult(result);
148 | }
149 | }
150 |
151 | public void afterHookedMethod(XServer_Param param) throws Throwable {
152 | super.beforeHookedMethod(param);
153 | if (tid > 0 && tid != Process.myPid()) return;
154 | gatherInfo(param);
155 | // 如果是自己调用的路过的信息,就忽略
156 | if(Invoke_New.isMe())return;
157 | Method m = methods.get(wss.get(this));
158 | result = param.getResult();
159 | JSONObject res = new JSONObject();
160 | res.put("type","after");
161 | res.put("method", Utils.getJavaName(m));
162 | res.put("isproxy",Invoke_New.isMe());
163 | res.put("threadid", Process.myTid());
164 |
165 | if(param.getThrowable() == null){
166 | res.put("result", ObjectHandler.saveObject(param.getResult()));
167 | }else {
168 | res.put("throws",ObjectHandler.briefObject(param.getThrowable()));
169 | }
170 |
171 | wss.get(this).trySend(res.toJSONString());
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/api/wsTracerNew.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.api;
2 |
3 | import android.os.Process;
4 | import android.util.Log;
5 |
6 | import com.alibaba.fastjson.JSON;
7 | import com.alibaba.fastjson.JSONArray;
8 | import com.alibaba.fastjson.JSONObject;
9 |
10 | import java.io.IOException;
11 | import java.lang.reflect.Method;
12 | import java.util.ArrayList;
13 | import java.util.HashMap;
14 |
15 | import monkeylord.XServer.XServer;
16 | import monkeylord.XServer.handler.ClassHandler;
17 | import monkeylord.XServer.handler.Hook.Unhook;
18 | import monkeylord.XServer.handler.Hook.XServer_MethodHook;
19 | import monkeylord.XServer.handler.Hook.XServer_Param;
20 | import monkeylord.XServer.handler.HookHandler;
21 | import monkeylord.XServer.handler.MethodHandler;
22 | import monkeylord.XServer.handler.ObjectHandler;
23 | import monkeylord.XServer.utils.DexHelper;
24 | import monkeylord.XServer.utils.NanoHTTPD;
25 | import monkeylord.XServer.utils.NanoWSD;
26 | import monkeylord.XServer.utils.Utils;
27 |
28 | //MassTracer的WebSocket处理重构,交互式。
29 | //TODO 完成增删改查,兼容之前接口,然后替换。
30 | public class wsTracerNew implements XServer.wsOperation {
31 | final boolean unhookOnClose = true;
32 | HashMap unhooks = new HashMap<>();
33 | ws websocket;
34 | hook hook;
35 |
36 | @Override
37 | public NanoWSD.WebSocket handle(NanoHTTPD.IHTTPSession handshake) { websocket = new ws(handshake);hook = new hook();return websocket; }
38 |
39 | public void handleMessage(String msg){
40 | JSONObject req = JSON.parseObject(msg);
41 | try {
42 | JSONObject call = new JSONObject();
43 | String[] methods ={};
44 | String[] classes = {};
45 | ArrayList clzArr = new ArrayList<>();
46 | ArrayList methodArr = new ArrayList<>();
47 | ArrayList errArr = new ArrayList<>();
48 | switch ((String) req.get("type")) {
49 | case "hook":
50 | methods = ((JSONArray) req.get("methods")).toArray(methods);
51 | for (String mtd:methods) {
52 | try {
53 | Method m = MethodHandler.getMethodbyJavaName(mtd);
54 | if (!unhooks.containsKey(mtd))
55 | unhooks.put(mtd, HookHandler.getProvider().hookMethod(m, hook));
56 | }catch (Throwable e){
57 | e.printStackTrace();
58 | errArr.add(mtd+":"+e.getMessage());
59 | }
60 | }
61 | call.put("type","hook");
62 | call.put("errors",errArr);
63 | call.put("hooks",unhooks.keySet());
64 | break;
65 | case "hookClass":
66 | classes = ((JSONArray) req.get("classes")).toArray(methods);
67 | for (String clzname:classes) {
68 | try {
69 | Class clz = Class.forName(clzname, false, XServer.classLoader);
70 | for (Method m : clz.getDeclaredMethods()) {
71 | String mtd = Utils.getJavaName(m);
72 | if (!unhooks.containsKey(mtd))
73 | unhooks.put(mtd, HookHandler.getProvider().hookMethod(m, hook));
74 | }
75 | }catch (Throwable e){
76 | e.printStackTrace();
77 | errArr.add(clzname+":"+e.getMessage());
78 | }
79 | }
80 | call.put("type","hook");
81 | call.put("errors",errArr);
82 | call.put("hooks",unhooks.keySet());
83 | break;
84 | case "unhook":
85 | methods = ((JSONArray) req.get("methods")).toArray(methods);
86 | for (String mtd:methods) {
87 | if(unhooks.containsKey(mtd))unhooks.remove(mtd).unhook();
88 | }
89 | call.put("type","unhook");
90 | call.put("hooks",unhooks.keySet());
91 | break;
92 | case "classes":
93 | call.put("type","classes");
94 | call.put("classes",DexHelper.getClassesInDex(XServer.classLoader));
95 | break;
96 | case "methods":
97 | classes = ((JSONArray) req.get("classes")).toArray(classes);
98 | for (String clz:classes) {
99 | try{
100 | for (Method method:Class.forName(clz,false,XServer.classLoader).getDeclaredMethods()) {
101 | methodArr.add(Utils.getJavaName(method));
102 | }
103 | clzArr.add(clz);
104 | }catch (Throwable e){
105 | e.printStackTrace();
106 | errArr.add(clz+" "+e.getLocalizedMessage());
107 | }
108 | }
109 | call.put("type","methods");
110 | call.put("methods",methodArr);
111 | call.put("errors",errArr);
112 | call.put("classes",clzArr);
113 | break;
114 | }
115 | websocket.trySend(call.toJSONString());
116 | }catch (Exception e){
117 | e.printStackTrace();
118 | }
119 | Log.e("[XServer Debug]", (String)req.get("type"));
120 | }
121 |
122 | // 包装一下就好,将显示相关的内容交给前端去处理
123 | private class hook extends XServer_MethodHook {
124 | boolean loopLockBefore = false;
125 | boolean loopLockAfter = false;
126 |
127 | @Override
128 | public void beforeHookedMethod(XServer_Param param) throws Throwable {
129 | super.beforeHookedMethod(param);
130 | if(loopLockBefore || loopLockAfter){
131 | Log.e("[XServer Debug]", "Avoiding Loop on " + Utils.getJavaName((Method) param.method));
132 | return;
133 | }
134 | try {
135 | loopLockBefore = true;
136 | JSONObject call = new JSONObject();
137 | call.put("type", "call");
138 | call.put("tid", Process.myTid());
139 | call.put("elapsed", Process.getElapsedCpuTime());
140 | call.put("method", Utils.getJavaName((Method) param.method));
141 | call.put("this", ObjectHandler.briefObject(param.thisObject));
142 | JSONArray params = new JSONArray();
143 | for (Object arg : param.args) {
144 | params.add(ObjectHandler.briefObject(arg));
145 | }
146 | call.put("params", params);
147 | websocket.trySend(call.toJSONString());
148 | }finally {
149 | loopLockBefore = false;
150 | }
151 | }
152 |
153 | @Override
154 | public void afterHookedMethod(XServer_Param param) throws Throwable {
155 | super.afterHookedMethod(param);
156 | if(loopLockBefore || loopLockAfter){
157 | Log.e("[XServer Debug]", "Avoiding Loop on " + Utils.getJavaName((Method) param.method));
158 | return;
159 | }
160 | try {
161 | loopLockAfter = true;
162 | JSONObject result = new JSONObject();
163 | result.put("type","result");
164 | result.put("tid", Process.myTid());
165 | result.put("elapsed", Process.getElapsedCpuTime());
166 | result.put("method",Utils.getJavaName((Method) param.method));
167 | result.put("this", ObjectHandler.briefObject(param.thisObject));
168 | result.put("result",ObjectHandler.briefObject(param.getResult()));
169 | if(param.hasThrowable())result.put("throw",ObjectHandler.briefObject(param.getThrowable()));
170 |
171 | websocket.trySend(result.toJSONString());
172 | }finally {
173 | loopLockAfter = false;
174 | }
175 | }
176 | }
177 |
178 | private class ws extends NanoWSD.WebSocket {
179 | public ws(NanoHTTPD.IHTTPSession handshakeRequest) {
180 | super(handshakeRequest);
181 | }
182 | public void trySend(String payload) {try {send(payload);} catch (IOException e) {e.printStackTrace();}}
183 | @Override
184 | protected void onMessage(NanoWSD.WebSocketFrame message) { handleMessage(message.getTextPayload()); }
185 | @Override
186 | protected void onOpen() {
187 | trySend("{\"type\":\"message\",\"message\":\"XServer wsTrace Connected.\"}");
188 | // 返回所有类
189 | JSONObject classes = new JSONObject();
190 | classes.put("type","classes");
191 | classes.put("classes",ClassHandler.getAllClasses(XServer.classLoader));
192 | }
193 | @Override
194 | protected void onClose(NanoWSD.WebSocketFrame.CloseCode code, String reason, boolean initiatedByRemote) {
195 | if (unhookOnClose) {
196 | for (String methodname : unhooks.keySet()) {
197 | unhooks.remove(methodname).unhook();
198 | }
199 | }
200 | }
201 | @Override
202 | protected void onPong(NanoWSD.WebSocketFrame pong) {}
203 | @Override
204 | protected void onException(IOException exception) {}
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/ClassHandler.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler;
2 |
3 | import com.alibaba.fastjson.JSON;
4 |
5 | import java.lang.reflect.Field;
6 | import java.lang.reflect.Member;
7 | import java.lang.reflect.Method;
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.Random;
12 |
13 | import monkeylord.XServer.XServer;
14 | import monkeylord.XServer.handler.Hook.XServer_MethodHook;
15 | import monkeylord.XServer.handler.Hook.XServer_Param;
16 | import monkeylord.XServer.utils.DexHelper;
17 | import monkeylord.XServer.utils.Utils;
18 |
19 | //处理类相关的内容
20 | public class ClassHandler {
21 | public static HashMap classLoaders = new HashMap<>();
22 |
23 | public static boolean monitorClassloaders(final ClassLoader classLoader){
24 | try {
25 | Class classLoaderClz = ClassLoader.class;
26 | Member m = classLoaderClz.getDeclaredConstructor(ClassLoader.class);
27 | HookHandler.getProvider().hookMethod(m, new XServer_MethodHook() {
28 | @Override
29 | public void beforeHookedMethod(XServer_Param param) throws Throwable {
30 | super.beforeHookedMethod(param);
31 | classLoaders.put("classloader"+ param.thisObject.hashCode(), (ClassLoader) param.thisObject);
32 | if(((ClassLoader) param.thisObject).getParent()!=null)classLoaders.put("classloader"+ ((ClassLoader) param.thisObject).getParent().hashCode(), (ClassLoader) ((ClassLoader) param.thisObject).getParent());
33 | }
34 | });
35 | return true;
36 | }catch (Exception e){
37 | e.printStackTrace();
38 | return false;
39 | }
40 | }
41 |
42 | public static boolean setXServerClassloader(String name){
43 | if(classLoaders.get(name)!=null){
44 | XServer.classLoader = classLoaders.get(name);
45 | return true;
46 | }
47 | return false;
48 | }
49 |
50 | public static HashMap getClassLoaderDescriptions(){
51 | HashMap map = new HashMap<>();
52 | for (Map.Entry entry:classLoaders.entrySet()) {
53 | try {
54 | map.put(entry.getKey(), entry.getValue().toString());
55 | }catch (Exception e){
56 | e.printStackTrace();
57 | map.put(entry.getKey(),entry.getValue().getClass().getName());
58 | }
59 | }
60 | return map;
61 | }
62 |
63 | public static String[] getAllClasses(ClassLoader classLoader) {
64 | return DexHelper.getClassesInDex(classLoader);
65 | }
66 |
67 | public static HashMap getClassDetail(Class clz) {
68 | HashMap detail = new HashMap();
69 | if (clz == null) return detail;
70 | detail.put("name", clz.getName());
71 | detail.put("methods", clz.getDeclaredMethods());
72 | detail.put("fields", clz.getDeclaredFields());
73 | detail.put("json", JSON.toJSON(clz));
74 | ArrayList methodDescriptions = new ArrayList();
75 | ArrayList fieldDescriptions = new ArrayList();
76 | for (Method method : clz.getDeclaredMethods()) {
77 | methodDescriptions.add(Utils.MethodDescription(method));
78 | }
79 | for (Field field : clz.getFields()) {
80 | fieldDescriptions.add(Utils.FieldDescription(field));
81 | }
82 | detail.put("methodList", methodDescriptions);
83 | detail.put("fieldList", fieldDescriptions);
84 |
85 | return detail;
86 | }
87 |
88 | public static Class findClassbyName(String clzName, ClassLoader classLoader) {
89 | try {
90 | return Class.forName(clzName, false, classLoader);
91 | } catch (ClassNotFoundException e) {
92 | return null;
93 | }
94 | }
95 |
96 | public static Class findClassbyJavaNameEx(String javaName, ClassLoader classLoader) {
97 | try {
98 | String clzName = javaName.replace('/', '.').replace(";", "");
99 | clzName = (clzName.charAt(0) == 'L') ? clzName.substring(1) : clzName;
100 | return Class.forName(clzName, false, classLoader);
101 | } catch (ClassNotFoundException e) {
102 | return null;
103 | }
104 | }
105 |
106 | /*
107 | * copied from libcore.reflect.InternalNames
108 | * */
109 | public static java.lang.Class> findClassbyJavaName(java.lang.String internalName,java.lang.ClassLoader classLoader) {
110 | if (internalName.startsWith("[")) {
111 | return java.lang.reflect.Array.newInstance(findClassbyJavaName(internalName.substring(1), classLoader), 0).getClass();
112 | }
113 | if (internalName.equals("Z")) {
114 | return java.lang.Boolean.TYPE;
115 | }
116 | if (internalName.equals("B")) {
117 | return java.lang.Byte.TYPE;
118 | }
119 | if (internalName.equals("S")) {
120 | return java.lang.Short.TYPE;
121 | }
122 | if (internalName.equals("I")) {
123 | return java.lang.Integer.TYPE;
124 | }
125 | if (internalName.equals("J")) {
126 | return java.lang.Long.TYPE;
127 | }
128 | if (internalName.equals("F")) {
129 | return java.lang.Float.TYPE;
130 | }
131 | if (internalName.equals("D")) {
132 | return java.lang.Double.TYPE;
133 | }
134 | if (internalName.equals("C")) {
135 | return java.lang.Character.TYPE;
136 | }
137 | if (internalName.equals("V")) {
138 | return java.lang.Void.TYPE;
139 | }
140 | java.lang.String name = internalName.substring(1, internalName.length() - 1).replace('/', '.');
141 | try {
142 | return classLoader.loadClass(name);
143 | } catch (java.lang.ClassNotFoundException e) {
144 | java.lang.NoClassDefFoundError error = new java.lang.NoClassDefFoundError(name);
145 | error.initCause(e);
146 | throw error;
147 | }
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/Hook/DummyProvider.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler.Hook;
2 |
3 | import java.lang.reflect.Member;
4 |
5 | import monkeylord.XServer.handler.HookHandler;
6 |
7 | // This is a dummy provider, if a hook framework cannot register its own class, it can hook and use this provider.
8 | public class DummyProvider implements HookHandler.HookProvider {
9 | @Override
10 | public Unhook hookMethod(Member hookMethod, XServer_MethodHook callback) {
11 | return null;
12 | }
13 |
14 | @Override
15 | public void unhookMethod(Member hookMethod, Object additionalObj) {
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/Hook/Unhook.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler.Hook;
2 |
3 | import monkeylord.XServer.handler.HookHandler;
4 |
5 | public class Unhook {
6 | private final java.lang.reflect.Member hookMethod;
7 | private final Object additionalObj;
8 |
9 | public Unhook(java.lang.reflect.Member hookMethod, Object additionalObj) {
10 | this.hookMethod = hookMethod;
11 | this.additionalObj = additionalObj;
12 | }
13 | public void unhook() {
14 | HookHandler.getProvider().unhookMethod(this.hookMethod, this.additionalObj);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/Hook/XServer_MethodHook.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler.Hook;
2 |
3 | public abstract class XServer_MethodHook {
4 | public void beforeHookedMethod(XServer_Param param) throws Throwable {}
5 | public void afterHookedMethod(XServer_Param param) throws Throwable {}
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/Hook/XServer_Param.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler.Hook;
2 |
3 | public class XServer_Param {
4 | // Copied from Xposed MethodHookParam
5 | public java.lang.Object[] args;
6 | public java.lang.reflect.Member method;
7 | public java.lang.Object result = null;
8 | public boolean returnEarly = false;
9 | public java.lang.Object thisObject;
10 | public java.lang.Throwable throwable = null;
11 |
12 | public java.lang.Object getResult() {
13 | return this.result;
14 | }
15 |
16 | public void setResult(java.lang.Object result) {
17 | this.result = result;
18 | this.throwable = null;
19 | this.returnEarly = true;
20 | }
21 |
22 | public java.lang.Throwable getThrowable() {
23 | return this.throwable;
24 | }
25 |
26 | public boolean hasThrowable() {
27 | return this.throwable != null;
28 | }
29 |
30 | public void setThrowable(java.lang.Throwable throwable) {
31 | this.throwable = throwable;
32 | this.result = null;
33 | this.returnEarly = true;
34 | }
35 |
36 | public java.lang.Object getResultOrThrowable() throws java.lang.Throwable {
37 | if (this.throwable == null) {
38 | return this.result;
39 | }
40 | throw this.throwable;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/HookHandler.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler;
2 |
3 | import monkeylord.XServer.handler.Hook.Unhook;
4 | import monkeylord.XServer.handler.Hook.XServer_MethodHook;
5 |
6 | //处理hook相关内容
7 | public class HookHandler {
8 | static HookProvider provider = null;
9 |
10 | public static HookProvider getProvider() throws NullPointerException{
11 | if(provider==null)throw new NullPointerException("No provider available");
12 | return provider;
13 | }
14 |
15 | public static boolean setProvider(HookProvider newProvider){
16 | if(provider!=null || newProvider==null)return false;
17 | provider=newProvider;
18 | return true;
19 | }
20 |
21 | public interface HookProvider{
22 | Unhook hookMethod(java.lang.reflect.Member hookMethod, XServer_MethodHook callback);
23 | void unhookMethod(java.lang.reflect.Member hookMethod, Object additionalObj);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/InterceptionHandler.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler;
2 |
3 | import android.util.Log;
4 |
5 | import com.alibaba.fastjson.JSONArray;
6 | import com.alibaba.fastjson.JSONObject;
7 |
8 | import java.io.File;
9 | import java.lang.reflect.Method;
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 |
14 | import monkeylord.XServer.api.Invoke_New;
15 | import monkeylord.XServer.handler.Hook.Unhook;
16 | import monkeylord.XServer.handler.Hook.XServer_MethodHook;
17 | import monkeylord.XServer.handler.Hook.XServer_Param;
18 | import monkeylord.XServer.utils.Utils;
19 | import monkeylord.XServer.utils.netUtil;
20 |
21 | public class InterceptionHandler {
22 | static InterceptionHandler instance = null;
23 | ArrayList shouldIntercept = new ArrayList();
24 | ArrayList methods = new ArrayList<>();
25 | File logFile;
26 | HashMap hooks = new HashMap();
27 | Interceptor interceptor;
28 |
29 | final String server = "http://127.0.0.1:8000";
30 |
31 | InterceptionHandler(){
32 | interceptor = new Interceptor();
33 | try {
34 | Class DexFile = Class.forName("dalvik.system.DexFile");
35 | Method defineClass = DexFile.getDeclaredMethod("defineClass",String.class, ClassLoader.class, long.class, List.class);
36 | HookHandler.getProvider().hookMethod(defineClass, new XServer_MethodHook() {
37 | @Override
38 | public void afterHookedMethod(XServer_Param param) throws Throwable {
39 | super.afterHookedMethod(param);
40 | if(param.getResult()==null)return;
41 | Log.e("TEST", param.args[0].toString());
42 | if(shouldIntercept.contains(param.args[0].toString())){
43 | Class loadedClass = (Class) param.getResult();
44 | for (Method method : loadedClass.getDeclaredMethods()) {
45 | if(methods.contains(Utils.getJavaName(method)))intercept(method);
46 | }
47 | }
48 | }
49 | });
50 | } catch (ClassNotFoundException e) {
51 | e.printStackTrace();
52 | } catch (NoSuchMethodException e) {
53 | e.printStackTrace();
54 | }
55 |
56 | }
57 |
58 | public void setInterceptMethods(String[] itcpMethods){
59 | for (String itcpMethod : itcpMethods) {
60 | shouldIntercept.add(itcpMethod.substring(itcpMethod.indexOf("L")+1,itcpMethod.indexOf(";")));
61 | methods.add(itcpMethod);
62 | }
63 | }
64 |
65 | public static InterceptionHandler getInstance(){
66 | if(instance==null)instance = new InterceptionHandler();
67 | return instance;
68 | }
69 |
70 | public void intercept(Method method){
71 | String javaname = Utils.getJavaName(method);
72 | Log.e("TEST", "intercept: "+javaname);
73 | if(hooks.get(javaname)!=null)return;
74 | else hooks.put(javaname,HookHandler.getProvider().hookMethod(method, interceptor));
75 | }
76 |
77 | public void stopIntercept(Method method){
78 | String javaname = Utils.getJavaName(method);
79 | if(hooks.get(javaname)==null)return;
80 | else hooks.remove(javaname).unhook();
81 | }
82 |
83 | class Interceptor extends XServer_MethodHook{
84 | @Override
85 | public void beforeHookedMethod(XServer_Param param) throws Throwable {
86 | super.beforeHookedMethod(param);
87 | if(Invoke_New.isMe())return;
88 |
89 | JSONObject call = new JSONObject();
90 | JSONArray params = new JSONArray();
91 | for (Object arg:param.args) { params.add(ObjectHandler.saveObject(arg)); }
92 | call.put("method", Utils.getJavaName((Method) param.method));
93 | if(param.thisObject!=null)call.put("this",ObjectHandler.saveObject(param.thisObject));
94 | call.put("params",params);
95 | ArrayList stacks=new ArrayList();
96 | for (StackTraceElement element:Thread.currentThread().getStackTrace()) {
97 | stacks.add(element.getClassName() + "." + element.getMethodName() + " : " + element.getLineNumber());
98 | }
99 | call.put("stackTrace",stacks);
100 | Object result = ObjectHandler.parseObject(new netUtil(server + "/invoke2", new org.json.JSONObject(call.toJSONString()).toString(2)).getRet());
101 | Log.e("[XServer Debug]", "result: " + result);
102 | if(result instanceof Invoke_New.XServerWrappedThrowable){
103 | if(((Invoke_New.XServerWrappedThrowable)result).shouldPassthough)
104 | param.setThrowable(((Invoke_New.XServerWrappedThrowable)result).getThrowable());
105 | }
106 | else param.setResult(result);
107 | }
108 | }
109 |
110 |
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/MemoryHandler.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler;
2 |
3 | import android.system.ErrnoException;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.File;
7 | import java.io.FileDescriptor;
8 | import java.io.FileInputStream;
9 | import java.io.IOException;
10 | import java.io.InputStreamReader;
11 | import java.lang.reflect.Constructor;
12 | import java.lang.reflect.InvocationTargetException;
13 | import java.lang.reflect.Method;
14 | import java.util.HashMap;
15 |
16 | public class MemoryHandler {
17 | static HashMap Memory = new HashMap<>();
18 | static HashMap OS = new HashMap<>();
19 | static Object osObject = null;
20 | static {
21 | try {
22 | // http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/libcore/io/Memory.java
23 | Class clzMemory = Class.forName("libcore.io.Memory");
24 | for (Method m:clzMemory.getDeclaredMethods()) {
25 | Memory.put(m.getName(),m);
26 | }
27 | // http://androidxref.com/9.0.0_r3/xref/libcore/luni/src/main/java/android/system/Os.java
28 | Class clzOS = null;
29 | try {
30 | clzOS = Class.forName("libcore.io.Linux");
31 | }catch (ClassNotFoundException e){
32 | // for Android 4.4
33 | clzOS = Class.forName("libcore.io.Posix");
34 | }
35 | Constructor osConstructor = clzOS.getDeclaredConstructor();
36 | osConstructor.setAccessible(true);
37 | osObject = osConstructor.newInstance();
38 | for (Method m:clzOS.getDeclaredMethods()) {
39 | OS.put(m.getName(),m);
40 | }
41 | } catch (ClassNotFoundException e) {
42 | e.printStackTrace();
43 | } catch (InstantiationException e) {
44 | e.printStackTrace();
45 | } catch (InvocationTargetException e) {
46 | e.printStackTrace();
47 | } catch (NoSuchMethodException e) {
48 | e.printStackTrace();
49 | } catch (IllegalAccessException e) {
50 | e.printStackTrace();
51 | }
52 | }
53 | public static byte[] readMemory(long address,int count) throws InvocationTargetException, IllegalAccessException {
54 | if(Memory.get("peekByteArray")==null)throw new IllegalAccessException();
55 | byte[] data=new byte[count];
56 | Memory.get("peekByteArray").invoke(null,address, data, 0, count);
57 | return data;
58 | }
59 | public static void writeMemory(long address,byte[] data) throws IllegalAccessException, InvocationTargetException {
60 | if(Memory.get("pokeByteArray")==null)throw new IllegalAccessException();
61 | byte[] mem = readMemory(address,data.length);
62 | for (int i = 0; i < mem.length; i++) {
63 | if(mem[i]!=data[i]){
64 | Memory.get("pokeByte").invoke(null,address+i,data[i]);
65 | }
66 | }
67 | //Memory.get("pokeByteArray").invoke(null,address, mem, 0, mem.length);
68 | }
69 |
70 | public static long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws IllegalAccessException, InvocationTargetException {
71 | if(OS.get("mmap")==null)throw new IllegalAccessException();
72 | return (long)OS.get("mmap").invoke(osObject,address,byteCount,prot,flags,fd,offset);
73 | }
74 | public static void munmap(long address, long byteCount) throws IllegalAccessException, InvocationTargetException {
75 | if(OS.get("munmap")==null)throw new IllegalAccessException();
76 | OS.get("munmap").invoke(osObject,address,byteCount);
77 | }
78 |
79 | public static String[] getMaps(){
80 | File maps=new File("/proc/self/maps");
81 | StringBuilder sb = new StringBuilder();
82 | try {
83 | FileInputStream is = new FileInputStream(maps);
84 | BufferedReader reader = new BufferedReader(new InputStreamReader(is));
85 | String str;
86 | while((str=reader.readLine())!=null){
87 | sb.append(str+"\r\n");
88 | }
89 | /*
90 | Process p = Runtime.getRuntime().exec("cat /proc/self/maps");
91 | p.waitFor();
92 | BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
93 | String str;
94 | while((str=reader.readLine())!=null){
95 | sb.append(str+"\r\n");
96 | }
97 | */
98 | } catch (IOException e) {
99 | e.printStackTrace();
100 | }
101 | String[] mapstrs=sb.toString().split("\r\n");
102 | for (int i = 0; i < mapstrs.length; i++) {
103 | String[] addrs =mapstrs[i].substring(0,17).split("-");
104 | long size = Long.parseLong(addrs[1],16)-Long.parseLong(addrs[0],16);
105 | mapstrs[i]=mapstrs[i].replaceFirst(" ",":"+Long.toHexString(size)+" ");
106 | }
107 | return mapstrs;
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/MethodHandler.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.serializer.SerializerFeature;
5 |
6 | import java.lang.reflect.Method;
7 | import java.util.ArrayList;
8 | import java.util.HashMap;
9 |
10 | import monkeylord.XServer.XServer;
11 | import monkeylord.XServer.utils.Utils;
12 |
13 | //处理方法相关内容
14 | public class MethodHandler {
15 | public static HashMap getMethodDetail(Method method) {
16 | HashMap detail = new HashMap();
17 | detail.put("name", method.getName());
18 | detail.put("class", method.getDeclaringClass().getName());
19 | detail.put("javaName", Utils.getJavaName(method));
20 | detail.put("parametersTypes", method.getParameterTypes());
21 | detail.put("returnType", method.getReturnType());
22 | detail.put("exceptionTypes", method.getExceptionTypes());
23 | detail.put("description", Utils.MethodDescription(method));
24 | detail.put("json", JSON.toJSONString(method, SerializerFeature.PrettyFormat));
25 | return detail;
26 | }
27 |
28 | public static Method getMethod(String className, String methodName) {
29 | try {
30 | for (Method method : Class.forName(className, false, XServer.classLoader).getMethods()) {
31 | if (method.getName().equals(methodName)) return method;
32 | }
33 | return null;
34 | } catch (ClassNotFoundException e) {
35 | e.printStackTrace();
36 | return null;
37 | }
38 | }
39 |
40 | public static Method getMethodbyJavaName(String javaName) {
41 | String classname=javaName.substring(0,javaName.indexOf("->"));
42 | String method=javaName.substring(javaName.indexOf("->")+2);
43 | Class clz=ClassHandler.findClassbyJavaName(classname,XServer.classLoader);
44 | if(clz==null)return null;
45 | for (Method m:clz.getDeclaredMethods()) {
46 | if(Utils.getJavaName(m).equals(javaName))return m;
47 | }
48 | return null;
49 | }
50 |
51 | public static Method[] getMethodsInClasses(Class[] clzs) {
52 | ArrayList methods = new ArrayList();
53 | for (Class clazz : clzs) {
54 | for (Method method : clazz.getDeclaredMethods()) {
55 | methods.add(method);
56 | }
57 | }
58 | return (Method[]) methods.toArray();
59 | }
60 |
61 | static Object invokeMethod(Method method, Object thisObj, Object[] params) {
62 | try {
63 | method.setAccessible(true);
64 | return ObjectHandler.storeObject(method.invoke(thisObj, params), "invokeResult");
65 | } catch (Exception e) {
66 | e.printStackTrace();
67 | return null;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/handler/ObjectHandler.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.handler;
2 |
3 | import java.io.Console;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.Random;
7 | import java.util.regex.Pattern;
8 |
9 | import monkeylord.XServer.XServer;
10 | import monkeylord.XServer.objectparser.GenericParser;
11 | import monkeylord.XServer.objectparser.StoredObjectParser;
12 | import monkeylord.XServer.utils.Utils;
13 |
14 | import static monkeylord.XServer.XServer.parsers;
15 |
16 | //处理对象相关内容
17 | public class ObjectHandler {
18 | public static HashMap objects = new HashMap();
19 |
20 | public static Object storeObject(Object obj, String name) {
21 | return objects.put(name, obj);
22 | }
23 |
24 | public static String saveObject(Object obj){
25 | if(obj==null)return "Null";
26 | XServer.ObjectParser parser = parsers.get(Utils.getTypeSignature(obj.getClass()));
27 | if(parser==null)parser=parsers.get("store");
28 | return Utils.getTypeSignature(obj.getClass())+"#"+parser.generate(obj);
29 | }
30 | public static String briefObject(Object obj){
31 | if(obj==null)return "Null";
32 | XServer.ObjectParser parser = parsers.get(Utils.getTypeSignature(obj.getClass()));
33 | if(parser==null)parser=parsers.get("generic");
34 | return Utils.getTypeSignature(obj.getClass())+"#"+parser.generate(obj);
35 | }
36 |
37 | public static Object getObject(String name) {
38 | return objects.get(name);
39 | }
40 |
41 | public static Object parseObject(String Object){
42 | if(Object.equals("Null"))return null;
43 | if(Object==null)return null;
44 | if(Object.indexOf("#")<0)return null;
45 | String type=Object.substring(0,Object.indexOf("#"));
46 | String raw=Object.substring(Object.indexOf("#")+1);
47 | XServer.ObjectParser parser = parsers.get(type);
48 | if(parser==null)parser=parsers.get("store");
49 | return parser.parse(raw);
50 | }
51 |
52 | public static Object[] getObjects(String name, String type) {
53 | return null;
54 | }
55 |
56 | public static Object removeObject(String name) {
57 | return objects.remove(name);
58 | }
59 |
60 | public static Object removeObject(Object object) {
61 | for (Map.Entry entry : objects.entrySet()) {
62 | if (entry.getValue().equals(object)) return objects.remove(entry.getKey());
63 | }
64 | return null;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/BooleanParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import monkeylord.XServer.XServer;
4 |
5 | public class BooleanParser implements XServer.ObjectParser{
6 |
7 | @Override
8 | public Object parse(String data) {
9 | return Boolean.parseBoolean(data);
10 | }
11 |
12 | @Override
13 | public String generate(Object obj) {
14 | return obj.toString();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/ByteArrayParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import android.util.Base64;
4 | import android.util.Log;
5 |
6 | import java.nio.Buffer;
7 | import java.util.Arrays;
8 |
9 | import monkeylord.XServer.XServer;
10 |
11 | public class ByteArrayParser implements XServer.ObjectParser {
12 | @Override
13 | public java.lang.Object parse(java.lang.String data) {
14 | if(data.indexOf('#')<0){
15 | return Base64.decode(data, Base64.DEFAULT);
16 | }else{
17 | String type=data.substring(0,data.indexOf("#"));
18 | String raw=data.substring(data.indexOf("#")+1);
19 | if(type.equals("raw"))return raw.getBytes();
20 | else if(type.equals("base64"))return Base64.decode(raw, Base64.DEFAULT);
21 | else return URLDecode(raw);
22 | }
23 | }
24 | @Override
25 | public String generate(Object obj) {
26 | Log.d("ByteArray", ""+new String((byte[])obj).getBytes().length+":"+((byte[])obj).length);
27 | if(Arrays.equals((byte[])obj,new String((byte[])obj).getBytes()))return "raw#"+new String((byte[])obj);
28 | //else return "base64#"+Base64.encodeToString((byte[])obj,Base64.DEFAULT);
29 | else return "URLEncoding#"+URLEncode((byte[])obj);
30 | }
31 | private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5',
32 | '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
33 | static public String URLEncode(byte[] bytes){
34 | StringBuffer sb=new StringBuffer();
35 | for (int i = 0; i < bytes.length; i++) {
36 | if((bytes[i]>='0'&&bytes[i]<='9')||(bytes[i]>='a'&&bytes[i]<='z')||(bytes[i]>='A'&&bytes[i]<='Z')||bytes[i]=='-'||bytes[i]=='_'||bytes[i]=='.'||bytes[i]=='~')
37 | sb.append((char)bytes[i]);
38 | else {
39 | sb.append('%');
40 | sb.append(HEX_CHAR[(256+bytes[i])/16%16]);
41 | sb.append(HEX_CHAR[(256+bytes[i])%16]);
42 | }
43 | }
44 | return sb.toString();
45 | }
46 | static public byte[] URLDecode(String str){
47 | byte[] org=str.getBytes();
48 | byte[] dst=new byte[org.length];
49 | byte[] fin;
50 | int i=0,j=0;
51 | for (i = 0; i < org.length; i++) {
52 | if(org[i]=='%'){
53 | dst[j]=(byte)Integer.parseInt(""+(char)org[i+1]+(char)org[i+2],16);
54 | j++;
55 | i=i+2;
56 | }else{
57 | dst[j]=org[i];
58 | j++;
59 | }
60 | }
61 | fin=new byte[j];
62 | for (j--; j >=0 ; j--) {
63 | fin[j]=dst[j];
64 | }
65 | return fin;
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/CollectionParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import com.alibaba.fastjson.JSONArray;
4 |
5 | import java.util.Collection;
6 | import java.util.HashMap;
7 | import java.util.List;
8 |
9 | import monkeylord.XServer.XServer;
10 | import monkeylord.XServer.handler.ObjectHandler;
11 |
12 | public class CollectionParser implements XServer.ObjectParser {
13 | public static HashMap objects = new HashMap();
14 | @Override
15 | public Object parse(String data) {
16 | String name=data.substring(0,data.indexOf("=>"));
17 | String array=data.substring(data.indexOf("=>")+2);
18 | Collection collection = (Collection) objects.get(name);
19 | collection.clear();
20 | for (Object item : JSONArray.parseArray(array)) {
21 | collection.add(ObjectHandler.parseObject(item.toString()));
22 | }
23 | return collection;
24 | }
25 |
26 | @Override
27 | public String generate(Object obj) {
28 | if(obj instanceof Collection){
29 | JSONArray array = new JSONArray();
30 | for (Object o : ((Collection) obj)) {
31 | array.add(ObjectHandler.saveObject(o));
32 | }
33 | objects.put(""+obj.hashCode(),obj);
34 | return obj.hashCode()+"=>"+array.toJSONString();
35 | }else return "Error Not Collection";
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/GenericParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import monkeylord.XServer.XServer;
4 |
5 | public class GenericParser implements XServer.ObjectParser {
6 | @Override
7 | public java.lang.Object parse(java.lang.String data) {
8 | //无法还原
9 | return null;
10 | }
11 |
12 | @Override
13 | public String generate(Object obj) {
14 | return obj.toString();
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/HashMapParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import android.util.Log;
4 |
5 | import com.alibaba.fastjson.JSON;
6 | import com.alibaba.fastjson.JSONArray;
7 | import com.alibaba.fastjson.JSONObject;
8 |
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.Set;
12 |
13 | import monkeylord.XServer.XServer;
14 | import monkeylord.XServer.handler.ObjectHandler;
15 |
16 | import static monkeylord.XServer.XServer.parsers;
17 |
18 | public class HashMapParser implements XServer.ObjectParser {
19 | public static HashMap objects = new HashMap();
20 | @Override
21 | public Object parse(String data) {
22 | //HashMap obj = new HashMap();
23 | String name=data.substring(0,data.indexOf("=>"));
24 | String kvs=data.substring(data.indexOf("=>")+2);
25 | HashMap map = (HashMap) objects.get(name);
26 | if(map==null)map = new HashMap<>();
27 | map.clear();
28 | for (Map.Entry entry : JSON.parseObject(kvs).entrySet()) {
29 | map.put(ObjectHandler.parseObject(entry.getKey()),ObjectHandler.parseObject(entry.getValue().toString()));
30 | }
31 | return map;
32 | }
33 |
34 | @Override
35 | public String generate(Object obj) {
36 | HashMap map = (HashMap) obj;
37 |
38 | JSONObject jsonObject = new JSONObject();
39 | for (Map.Entry entry: (Set)map.entrySet()) {
40 | Log.e("[XServer]", entry.getKey().toString());
41 | Log.e("[XServer]", entry.getValue().toString());
42 | jsonObject.put(ObjectHandler.saveObject(entry.getKey()),ObjectHandler.saveObject(entry.getValue()));
43 | }
44 |
45 | objects.put(""+obj.hashCode(),obj);
46 | Log.e("[XServer]", jsonObject.toJSONString());
47 | Log.e("[XServer]", jsonObject.toString());
48 | return obj.hashCode()+"=>"+jsonObject.toJSONString();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/IntParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import monkeylord.XServer.XServer;
4 |
5 | public class IntParser implements XServer.ObjectParser {
6 | @Override
7 | public java.lang.Object parse(java.lang.String data) {
8 | return Integer.parseInt(data);
9 | }
10 |
11 | @Override
12 | public String generate(Object obj) {
13 | return obj.toString();
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/JSONParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import monkeylord.XServer.XServer;
4 |
5 | public class JSONParser implements XServer.ObjectParser {
6 | @Override
7 | public java.lang.Object parse(java.lang.String data) {
8 | //TODO
9 | return null;
10 | }
11 |
12 | @Override
13 | public String generate(Object obj) {
14 | return null;
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/StoredObjectParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import android.util.Log;
4 |
5 | import com.alibaba.fastjson.JSON;
6 |
7 | import java.lang.reflect.Field;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 | import java.util.Random;
11 |
12 | import monkeylord.XServer.XServer;
13 | import monkeylord.XServer.handler.ObjectHandler;
14 | import monkeylord.XServer.utils.Utils;
15 |
16 | import static monkeylord.XServer.XServer.parsers;
17 |
18 | public class StoredObjectParser implements XServer.ObjectParser {
19 | public static HashMap objects = new HashMap();
20 | @Override
21 | public Object parse(String data) {
22 | String objname=data.substring(0,data.indexOf("=>"));
23 | String fieldmapStr=data.substring(data.indexOf("=>")+2);
24 | Object obj = objects.get(objname);
25 | if(obj==null)obj = ObjectHandler.objects.get(objname);
26 | if(obj==null)return null;
27 | HashMap fieldmap = (HashMap)JSON.parseObject(fieldmapStr,HashMap.class);
28 | if(!fieldmap.isEmpty()){
29 | try {
30 | Class objClass = obj.getClass();
31 | for (Map.Entry entry : fieldmap.entrySet()) {
32 | Field field = objClass.getDeclaredField(entry.getKey());
33 | field.setAccessible(true);
34 | field.set(obj,ObjectHandler.parseObject(entry.getValue()));
35 | }
36 | }catch (Exception e){
37 | Log.e("XServer", "SOParser: " + e.getLocalizedMessage() );
38 | }
39 | }
40 | return obj;
41 | }
42 |
43 | @Override
44 | public String generate(Object obj) {
45 | String objname;
46 | try{
47 | objname=""+obj.hashCode();
48 | }catch (Exception e){
49 | objname= ""+new Random().nextLong();
50 | }
51 | HashMap fieldmap = new HashMap();
52 | try{
53 | Field fields[] = obj.getClass().getDeclaredFields();
54 | for (Field field:fields) {
55 | // 只处理public属性
56 | // Handle public field only
57 | //if(field.isAccessible()){
58 | field.setAccessible(true);
59 | Object fieldObj = field.get(obj);
60 | Log.i("XServer", "SOParser Field: " + field.getName() + "@" + Utils.getTypeSignature(fieldObj.getClass()));
61 | // 只处理原始类型,避免循环引用
62 | // Handle primitive type only, avoid cell.
63 | if(fieldObj.getClass().isPrimitive()||parsers.get(Utils.getTypeSignature(fieldObj.getClass()))!=null){
64 | fieldmap.put(field.getName(),ObjectHandler.saveObject(fieldObj));
65 | }
66 | //}
67 | }
68 | }catch (Exception e){
69 | Log.e("XServer", "SOParser: " + e.getLocalizedMessage() );
70 | }
71 | String fieldmapStr = JSON.toJSONString(fieldmap,true);
72 | objects.put(""+objname,obj);
73 | return objname+"=>"+fieldmapStr;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/objectparser/StringParser.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import monkeylord.XServer.XServer;
4 |
5 | public class StringParser implements XServer.ObjectParser {
6 | @Override
7 | public Object parse(java.lang.String data) {
8 | return data;
9 | }
10 |
11 | @Override
12 | public String generate(Object obj) {
13 | return obj.toString();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/utils/DexHelper.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.utils;
2 |
3 | import java.lang.reflect.Array;
4 | import java.lang.reflect.Field;
5 | import java.lang.reflect.Method;
6 |
7 | public class DexHelper {
8 | public static String[] getClassesInDex(ClassLoader CL) {
9 | String[] result = {};
10 | try {
11 | Field field3 = CL.getClass().getSuperclass().getDeclaredField("pathList");
12 | field3.setAccessible(true);
13 | Object pathList = field3.get(CL);
14 | Field field2 = pathList.getClass().getDeclaredField("dexElements");
15 | field2.setAccessible(true);
16 | Object elements = field2.get(pathList);
17 | for (int i = 0; i < Array.getLength(elements); i++) {
18 | Object element = Array.get(elements, i);
19 | Field field1 = element.getClass().getDeclaredField("dexFile");
20 | field1.setAccessible(true);
21 | Object DexFile = field1.get(element);
22 | //XposedBridge.log(DexFile.getClass().getName());
23 | for (Method m : DexFile.getClass().getDeclaredMethods()) {
24 | // XposedBridge.log("DEXFILE:"+m.getName());
25 | if (m.getName().equalsIgnoreCase("getClassNameList")) {
26 | m.setAccessible(true);
27 | Field field = DexFile.getClass().getDeclaredField("mCookie");
28 | field.setAccessible(true);
29 | Object clist = m.invoke(DexFile,
30 | field.get(DexFile));
31 | int length1 = result.length;
32 | int length2 = ((String[]) clist).length;
33 | int totalLength = length1 + length2;
34 | String[] totalArr = new String[totalLength];
35 | for (int i1 = 0; i1 < length1; i1++) {
36 | totalArr[i1] = result[i1];
37 | }
38 | for (int i1 = 0; i1 < length2; i1++) {
39 | totalArr[i1 + length1] = ((String[]) clist)[i1];
40 | }
41 | result = totalArr;
42 | //return (String[]) clist;
43 | }
44 | }
45 | }
46 | } catch (Exception e) {
47 |
48 | } finally {
49 | return result;
50 | }
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.utils;
2 |
3 | import com.blankj.utilcode.util.NetworkUtils;
4 |
5 | import java.lang.reflect.Field;
6 | import java.lang.reflect.Method;
7 | import java.lang.reflect.Modifier;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | import static java.lang.reflect.Modifier.STATIC;
12 |
13 | public class Utils {
14 | private static final Map, String> PRIMITIVE_TO_SIGNATURE;
15 |
16 | static {
17 | PRIMITIVE_TO_SIGNATURE = new HashMap, String>(9);
18 | PRIMITIVE_TO_SIGNATURE.put(byte.class, "B");
19 | PRIMITIVE_TO_SIGNATURE.put(char.class, "C");
20 | PRIMITIVE_TO_SIGNATURE.put(short.class, "S");
21 | PRIMITIVE_TO_SIGNATURE.put(int.class, "I");
22 | PRIMITIVE_TO_SIGNATURE.put(long.class, "J");
23 | PRIMITIVE_TO_SIGNATURE.put(float.class, "F");
24 | PRIMITIVE_TO_SIGNATURE.put(double.class, "D");
25 | PRIMITIVE_TO_SIGNATURE.put(void.class, "V");
26 | PRIMITIVE_TO_SIGNATURE.put(boolean.class, "Z");
27 | }
28 |
29 | public static String getMyIp(){
30 | try {
31 | return NetworkUtils.getIPAddress(true);
32 | }catch (Exception e){
33 | return "127.0.0.1";
34 | }
35 | }
36 |
37 | public static String MethodDescription(Method m) {
38 | StringBuilder sb = new StringBuilder();
39 | sb.append(Modifier.toString(m.getModifiers()));
40 | sb.append(" ");
41 | sb.append(m.getReturnType().getName());
42 | sb.append(" ");
43 | sb.append(m.getName());
44 | sb.append("(");
45 | for (int i = 0; i < m.getParameterTypes().length; i++) {
46 | if (i != 0) sb.append(",");
47 | sb.append(m.getParameterTypes()[i].getName());
48 | sb.append(" param" + i);
49 | }
50 | sb.append(")");
51 | if (m.getExceptionTypes().length > 0) {
52 | sb.append("throws ");
53 | boolean first = true;
54 | for (Class> type : m.getExceptionTypes()) {
55 | if (!first) sb.append(",");
56 | else first = false;
57 | sb.append(type.getName());
58 | }
59 | }
60 | return sb.toString();
61 | }
62 |
63 | public static String FieldDescription(Field field) {
64 | String s = Modifier.toString(field.getModifiers()) + " " + field.getType() + " " + field.getName();
65 |
66 | if ((field.getModifiers() & STATIC) != 0) {
67 | try {
68 | field.setAccessible(true);
69 | s += " :: " + field.get(null);
70 | } catch (IllegalAccessException e) {
71 | e.printStackTrace();
72 | }
73 | }
74 | return s;
75 | }
76 |
77 | public static String getJavaName(Method method) {
78 | StringBuilder result = new StringBuilder();
79 | result.append(getTypeSignature(method.getDeclaringClass()));
80 | result.append("->");
81 | result.append(method.getName());
82 | result.append(getMethodSignature(method));
83 | return result.toString();
84 | }
85 |
86 | public static String getMethodSignature(Method method) {
87 | StringBuilder result = new StringBuilder();
88 |
89 | result.append('(');
90 | Class>[] parameterTypes = method.getParameterTypes();
91 | for (Class> parameterType : parameterTypes) {
92 | result.append(getTypeSignature(parameterType));
93 | }
94 | result.append(')');
95 | result.append(getTypeSignature(method.getReturnType()));
96 |
97 | return result.toString();
98 | }
99 |
100 | public static String getTypeSignature(Class> clazz) {
101 | String primitiveSignature = PRIMITIVE_TO_SIGNATURE.get(clazz);
102 | if (primitiveSignature != null) {
103 | return primitiveSignature;
104 | } else if (clazz.isArray()) {
105 | return "[" + getTypeSignature(clazz.getComponentType());
106 | } else {
107 | // TODO: this separates packages with '.' rather than '/'
108 | return "L" + clazz.getName() + ";";
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/utils/Xlog.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.utils;
2 |
3 | import java.io.FileWriter;
4 | import java.io.IOException;
5 | import java.io.PrintWriter;
6 |
7 | public class Xlog {
8 | PrintWriter xlog;
9 | int count = 0;
10 |
11 | public Xlog(String logfile) throws IOException {
12 | xlog = new PrintWriter(new FileWriter(logfile, false));
13 | }
14 |
15 | public void log(String line) {
16 | xlog.println(line);
17 | if (count++ > 20) xlog.flush();
18 | }
19 |
20 | public void log(String line, boolean flush) {
21 | xlog.println(line);
22 | if (flush) xlog.flush();
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/monkeylord/XServer/utils/netUtil.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.utils;
2 |
3 | import java.io.BufferedInputStream;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.PrintWriter;
6 | import java.net.HttpURLConnection;
7 | import java.net.InetSocketAddress;
8 | import java.net.MalformedURLException;
9 | import java.net.Proxy;
10 | import java.net.ProxySelector;
11 | import java.net.URI;
12 | import java.net.URISyntaxException;
13 | import java.net.URL;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 |
18 | /**
19 | * Created by secneo on 2017/3/6.
20 | */
21 | public class netUtil extends Thread {
22 | URL url;
23 | String postData;
24 | String ret;
25 | String error;
26 | List proxys;
27 | int status;
28 |
29 | public netUtil(String URL, String data) {
30 | try {
31 | postData = new String(data);
32 | ret = new String(postData);
33 | url = new URL(URL);
34 | error = null;
35 | proxys = ProxySelector.getDefault().select(new URI("http://www.baidu.com"));
36 | } catch (MalformedURLException e) {
37 | error = "MalformedURLException";
38 | } catch (URISyntaxException e) {
39 | error = "URISyntaxException";
40 | }
41 |
42 | }
43 | public netUtil(String URL, String data,List proxies) {
44 | this(URL,data);
45 | if(proxies==null)proxys=new ArrayList();
46 | else proxys=proxies;
47 | }
48 |
49 | public void run() {
50 | doPost();
51 | }
52 |
53 | public String getRet() {
54 | try {
55 | start();
56 | join();
57 | } catch (InterruptedException e) {
58 | error = "InterruptedException";
59 | }
60 | return ret;
61 | }
62 |
63 | public boolean isError() {
64 | if (error.equals("")) return true;
65 | else return false;
66 | }
67 |
68 | public String getError() {
69 | return error;
70 | }
71 |
72 | private void doPost() {
73 | try {
74 | //proxys.add(new Proxy(Proxy.Type.HTTP,new InetSocketAddress("127.0.0.1",8080)));
75 | HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection((proxys.size()>0)?proxys.get(0):Proxy.NO_PROXY);
76 | httpURLConnection.setRequestMethod("POST");// 提交模式
77 | httpURLConnection.setRequestProperty("Content-Type","application/json; charset=utf-8");
78 | httpURLConnection.setConnectTimeout(10000);//连接超时 单位毫秒
79 | //httpURLConnection.setReadTimeout(2000);//读取超时 单位毫秒
80 | // 发送POST请求必须设置如下两行
81 | httpURLConnection.setDoOutput(true);
82 | httpURLConnection.setDoInput(true);
83 | // 获取URLConnection对象对应的输出流
84 | PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
85 | // 发送请求参数
86 | printWriter.write(postData);//post的参数 xx=xx&yy=yy
87 | // flush输出流的缓冲
88 | printWriter.flush();
89 | //开始获取数据
90 | status = httpURLConnection.getResponseCode();
91 | BufferedInputStream bis = new BufferedInputStream((httpURLConnection.getResponseCode()!=200)?httpURLConnection.getErrorStream():httpURLConnection.getInputStream());
92 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
93 | int len;
94 | byte[] arr = new byte[1024];
95 | while ((len = bis.read(arr)) != -1) {
96 | bos.write(arr, 0, len);
97 | bos.flush();
98 | }
99 | bos.close();
100 | ret = bos.toString();
101 | } catch (Exception e) {
102 | error = "Exception";
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | XServer
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/monkeylord/XServer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer;
2 |
3 | import org.junit.Test;
4 |
5 | import monkeylord.XServer.handler.ObjectHandler;
6 | import monkeylord.XServer.objectparser.IntParser;
7 | import monkeylord.XServer.utils.Utils;
8 |
9 | import static monkeylord.XServer.XServer.parsers;
10 | import static org.junit.Assert.assertEquals;
11 |
12 | /**
13 | * Example local unit test, which will execute on the development machine (host).
14 | *
15 | * @see Testing documentation
16 | */
17 | public class ExampleUnitTest {
18 | @Test
19 | public void addition_isCorrect() throws Exception {
20 | parsers.put("I",new IntParser());
21 | System.out.print(Utils.getTypeSignature(int.class));
22 | System.out.print(Utils.getTypeSignature(byte[].class));
23 | System.out.print(Utils.getTypeSignature(String.class));
24 | System.out.println(ObjectHandler.parseObject("I#24"));
25 | assertEquals(4, 2 + 2);
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/test/java/monkeylord/XServer/objectparser/ByteArrayParserTest.java:
--------------------------------------------------------------------------------
1 | package monkeylord.XServer.objectparser;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | public class ByteArrayParserTest {
8 |
9 | @Test
10 | public void URLEncode() {
11 | byte[] toEncode={3,' ','a',56,21,'A','.','#',0x33,-80};
12 | System.out.println("Encode:");
13 | System.out.println(ByteArrayParser.URLEncode(toEncode));
14 | }
15 |
16 | @Test
17 | public void URLDecode() {
18 | String toDecode="%f0%23 1234.sa%20a8%2aA.%233";
19 | System.out.println("Decode:");
20 | System.out.println(new String(ByteArrayParser.URLDecode(toDecode)));
21 | System.out.println(ByteArrayParser.URLEncode(ByteArrayParser.URLDecode(toDecode)));
22 | }
23 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.4.10'
5 |
6 | repositories {
7 | jcenter()
8 | google()
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.4.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | configurations.all {
20 | resolutionStrategy {
21 | force "com.android.support:appcompat-v7:26.0.0"
22 | force "com.android.support:support-v4:26.0.0"
23 | }
24 | }
25 | allprojects {
26 | repositories {
27 | jcenter()
28 | google()
29 | }
30 | }
31 |
32 | task clean(type: Delete) {
33 | delete rootProject.buildDir
34 | }
35 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/rhino-android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/rhino-android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 26
7 |
8 |
9 | defaultConfig {
10 | minSdkVersion 19
11 | targetSdkVersion 26
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 |
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | release_x86 {
25 |
26 | }
27 | release_ndk_all {
28 | }
29 | debugWithMinify {}
30 | }
31 | compileOptions {
32 | sourceCompatibility = '1.8'
33 | targetCompatibility = '1.8'
34 | }
35 |
36 | }
37 | dependencies {
38 | implementation fileTree(include: ['*.jar'], dir: 'libs')
39 | implementation 'com.android.support:appcompat-v7:26.1.0'
40 | implementation 'com.android.support:support-v4:26.1.0'
41 |
42 | implementation 'com.android.support:support-annotations:26.0.0'
43 | api 'org.mozilla:rhino:1.7.10'
44 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
45 | }
46 |
--------------------------------------------------------------------------------
/rhino-android/libs/dx.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/monkeylord/XServer/1bd4c57b241275aa432672e42e4e81d6f3cdf32f/rhino-android/libs/dx.jar
--------------------------------------------------------------------------------
/rhino-android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/rhino-android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/api/AbsApi.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.api;
2 |
3 | import org.mozilla.javascript.ScriptableObject;
4 |
5 | /**
6 | * Created by 17719247306 on 2018/8/28
7 | */
8 | public abstract class AbsApi {
9 |
10 | protected abstract String[] funs();
11 | public void define(ScriptableObject global) {
12 | global.defineFunctionProperties(funs(), this.getClass(),
13 | ScriptableObject.DONTENUM);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/api/RhinoApi.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.api;
2 |
3 | import android.util.Log;
4 |
5 | import org.mozilla.javascript.Context;
6 | import org.mozilla.javascript.Function;
7 | import org.mozilla.javascript.NativeJavaArray;
8 | import org.mozilla.javascript.Scriptable;
9 | import org.mozilla.javascript.annotations.JSFunction;
10 |
11 | import java.lang.reflect.Field;
12 | import java.util.Arrays;
13 | import java.util.HashSet;
14 | import java.util.Set;
15 |
16 | import cn.vove7.rhino.common.GcCollector;
17 |
18 | /**
19 | * Created by 17719247306 on 2018/8/28
20 | */
21 | public class RhinoApi extends AbsApi {
22 |
23 | @Override
24 | protected String[] funs() {
25 | return new String[]{
26 | //"loadAsset",
27 | "quit",
28 | "print",
29 | "log",
30 | };
31 | }
32 |
33 | public static void quit(Context cx, Scriptable thisObj,//global
34 | Object[] args, Function funObj) {
35 | System.err.println("over");
36 | GcCollector.gc(thisObj);
37 | }
38 | //
39 | ///**
40 | // * 从Asset加载
41 | // */
42 | //
43 | //public static void loadAsset(Context cx, Scriptable thisObj,
44 | // Object[] args, Function funObj) {
45 | // for (Object arg : args) {
46 | // String file = Context.toString(arg);
47 | // try {
48 | // Log.d("RhinoApi :", "loadAsset ----> " + file);
49 | // Reader reader = new InputStreamReader(ctx.getAssets().open(file));
50 | // cx.evaluateReader(thisObj, reader, "load_" + file, 1, null);
51 | // } catch (Exception ex) {
52 | // onException(ex);
53 | // }
54 | // }
55 | //}
56 |
57 | public static void onException(Throwable e) {
58 | notifyOutput(OnPrint.ERROR, e.getMessage());
59 | }
60 |
61 | public interface OnPrint {
62 | int ERROR = Log.ERROR;
63 | int LOG = Log.INFO;
64 |
65 | void onPrint(int level, String msg);
66 | }
67 |
68 | private static final Set printList = new HashSet<>();
69 |
70 | public static void regPrint(OnPrint print) {
71 | synchronized (printList) {
72 | printList.add(print);
73 | }
74 | }
75 |
76 | public static void unregPrint(OnPrint print) {
77 | synchronized (printList) {
78 | printList.remove(print);
79 | }
80 | }
81 |
82 | private static void notifyOutput(int l, String o) {
83 | synchronized (printList) {
84 | for (OnPrint p : printList) {
85 | p.onPrint(l, o);
86 | }
87 | }
88 | }
89 |
90 | @JSFunction
91 | public static void log(Context cx, Scriptable thisObj,
92 | Object[] args, Function funObj) {
93 | Log.d("Rhino", Arrays.toString(args));
94 | }
95 |
96 |
97 | @JSFunction
98 | public synchronized static void print(Context cx, Scriptable thisObj,
99 | Object[] args, Function funObj) {
100 | StringBuilder builder = new StringBuilder();
101 | for (int i = 0; i < args.length; i++) {
102 |
103 | if (i > 0)
104 | builder.append(' ');
105 | // Convert the arbitrary JavaScript value into a string form.
106 | Object obj = args[i];
107 | String s;
108 | if (obj instanceof NativeJavaArray) {
109 | try {
110 | Field ar = obj.getClass().getDeclaredField("array");
111 | ar.setAccessible(true);
112 | s = Arrays.toString((Object[]) ar.get(obj));
113 | } catch (Exception e) {
114 | s = Context.toString(obj);
115 | e.printStackTrace();
116 | }
117 | } else {
118 | s = Context.toString(obj);
119 | }
120 | builder.append(s);
121 | }
122 | builder.append("\n");
123 | doLog(builder.toString());
124 | //Log.d("Vove :", " out ----> " + builder.toString());
125 | }
126 |
127 | public static void doLog(String m) {
128 | notifyOutput(OnPrint.LOG, m);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/api/ThreadApi.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.api;
2 |
3 | import org.mozilla.javascript.Scriptable;
4 |
5 | import cn.vove7.rhino.common.GcCollector;
6 |
7 | /**
8 | * Created by Vove on 2018/8/28
9 | */
10 | public class ThreadApi extends AbsApi {
11 | @Override
12 | protected String[] funs() {
13 | return new String[0];
14 | }
15 |
16 | public static Thread start(Scriptable scrop, Runnable runnable) {
17 | Thread t = new WrappedThread(runnable);
18 | t.start();
19 | GcCollector.reg(scrop, t);
20 | return t;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/api/WrappedThread.java:
--------------------------------------------------------------------------------
1 |
2 |
3 | package cn.vove7.rhino.api;
4 |
5 |
6 | import android.os.Handler;
7 | import android.os.Looper;
8 | import android.support.annotation.Nullable;
9 |
10 | import org.mozilla.javascript.WrappedException;
11 |
12 | /**
13 | * Handy class for starting a new thread that has a looper. The looper can then be
14 | * used to create handler classes. Note that start() must still be called.
15 | */
16 | public class WrappedThread extends Thread {
17 |
18 | Looper mLooper;
19 | private @Nullable
20 | Handler mHandler;
21 | Runnable runnable;
22 |
23 | public WrappedThread(String name) {
24 | super(name);
25 |
26 | }
27 |
28 | public WrappedThread(Runnable runnable) {
29 | this.runnable = runnable;
30 | }
31 |
32 | /**
33 | * Call back method that can be explicitly overridden if needed to execute some
34 | * setup before Looper loops.
35 | */
36 | protected void onLooperPrepared() {
37 | mHandler = new Handler(getLooper());
38 | mHandler.post(runnable);
39 | }
40 |
41 | @Override
42 | public void run() {
43 | //mTid = Process.myTid();
44 | Looper.prepare();
45 |
46 | synchronized (this) {
47 | mLooper = Looper.myLooper();
48 | notifyAll();
49 | }
50 | onLooperPrepared();
51 | try {
52 | Looper.loop();
53 | } catch (WrappedException ie) {
54 | Throwable e = ie.getWrappedException();
55 | if (!(e instanceof InterruptedException)) {
56 | RhinoApi.onException(e);
57 | }
58 | } catch (Exception e) {
59 | // e.printStackTrace();
60 | RhinoApi.onException(e);
61 | }
62 |
63 | }
64 |
65 | /**
66 | * This method returns the Looper associated with this thread. If this thread not been started
67 | * or for any reason isAlive() returns false, this method will return null. If this thread
68 | * has been started, this method will block until the looper has been initialized.
69 | *
70 | * @return The looper.
71 | */
72 | public Looper getLooper() {
73 | if (!isAlive()) {
74 | return null;
75 | }
76 |
77 | // If the thread has been started, wait until the looper has been created.
78 | synchronized (this) {
79 | while (isAlive() && mLooper == null) {
80 | try {
81 | wait();
82 | } catch (InterruptedException e) {
83 | }
84 | }
85 | }
86 | return mLooper;
87 | }
88 |
89 |
90 | public Handler getThreadHandler() {
91 | if (mHandler == null) {
92 | mHandler = new Handler(getLooper());
93 | }
94 | return mHandler;
95 | }
96 |
97 |
98 | public boolean quit() {
99 | Looper looper = getLooper();
100 | if (looper != null) {
101 | looper.quit();
102 | return true;
103 | }
104 | return false;
105 | }
106 |
107 |
108 | public boolean quitSafely() {
109 | Looper looper = getLooper();
110 | if (looper != null) {
111 | looper.quitSafely();
112 | return true;
113 | }
114 | return false;
115 | }
116 |
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/AndroidContextFactory.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.common;
2 |
3 |
4 |
5 | import android.os.Build;
6 | import android.support.annotation.VisibleForTesting;
7 |
8 | import org.mozilla.javascript.Context;
9 | import org.mozilla.javascript.tools.shell.ShellContextFactory;
10 |
11 | import java.io.File;
12 |
13 | /**
14 | * Ensures that the classLoader used is correct
15 | *
16 | * @author F43nd1r
17 | * @since 11.01.2016
18 | */
19 |
20 | public class AndroidContextFactory extends ShellContextFactory {
21 | private final File cacheDirectory;
22 |
23 | /**
24 | * Create a new factory. It will cache generated code in the given directory
25 | *
26 | * @param cacheDirectory the cache directory
27 | */
28 | public AndroidContextFactory(File cacheDirectory) {
29 | this.cacheDirectory = cacheDirectory;
30 | initApplicationClassLoader(createClassLoader(AndroidContextFactory.class.getClassLoader()));
31 | }
32 |
33 | /**
34 | * Create a ClassLoader which is able to deal with bytecode
35 | *
36 | * @param parent the parent of the create classloader
37 | * @return a new ClassLoader
38 | */
39 | /**
40 | * Create a ClassLoader which is able to deal with bytecode
41 | *
42 | * @param parent the parent of the create classloader
43 | * @return a new ClassLoader
44 | */
45 | @VisibleForTesting
46 | @Override
47 | public BaseAndroidClassLoader createClassLoader(ClassLoader parent) {
48 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
49 | return new InMemoryAndroidClassLoader(parent);
50 | }
51 | return new FileAndroidClassLoader(parent, cacheDirectory);
52 | }
53 |
54 |
55 | @Override
56 | protected void observeInstructionCount(Context cx, int instructionCount) {
57 | if (Thread.currentThread().isInterrupted()) {
58 | throw new RuntimeException("Interrupted");
59 | }
60 | }
61 |
62 | @Override
63 | protected Context makeContext() {
64 | Context cx = super.makeContext();
65 | cx.setInstructionObserverThreshold(10000);
66 | return cx;
67 | }
68 |
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/AssetAndUrlModuleSourceProvider.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.common;
2 |
3 | import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
4 | import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStreamReader;
8 | import java.net.URI;
9 | import java.net.URISyntaxException;
10 | import java.util.Arrays;
11 | import java.util.List;
12 |
13 | /**
14 | * ...
15 | * Created by Stardust on 2017/5/9.
16 | * 重定向require资源
17 | */
18 |
19 | public class AssetAndUrlModuleSourceProvider extends UrlModuleSourceProvider {
20 |
21 | private static final String MODULES_PATH = "rhino_require";
22 | private android.content.Context mContext;
23 | private List mModules;
24 | private final URI mBaseURI = URI.create("file:///android_asset/" + MODULES_PATH);
25 |
26 | public AssetAndUrlModuleSourceProvider(android.content.Context context, List list) {
27 | super(list, null);
28 | mContext = context;
29 | try {
30 | mModules = Arrays.asList(mContext.getAssets().list(MODULES_PATH));
31 | } catch (IOException e) {
32 | throw new RuntimeException(e);
33 | }
34 | }
35 |
36 | @Override
37 | protected ModuleSource loadFromPrivilegedLocations(String moduleId, Object validator) throws IOException, URISyntaxException {
38 | String moduleIdWithExtension = moduleId;
39 | if (!moduleIdWithExtension.endsWith(".js")) {
40 | moduleIdWithExtension += ".js";
41 | }
42 | if (mModules.contains(moduleIdWithExtension)) {
43 | return new ModuleSource(new InputStreamReader(mContext.getAssets()
44 | .open(MODULES_PATH + "/" + moduleIdWithExtension)), null,
45 | URI.create(moduleIdWithExtension), mBaseURI, validator);
46 | }
47 | return super.loadFromPrivilegedLocations(moduleId, validator);
48 | }
49 | }
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/BaseAndroidClassLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2016 Lukas Morawietz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.vove7.rhino.common;
18 |
19 | import android.support.annotation.NonNull;
20 | import android.support.annotation.Nullable;
21 |
22 | import com.android.dex.Dex;
23 | import com.android.dx.cf.direct.DirectClassFile;
24 | import com.android.dx.cf.direct.StdAttributeFactory;
25 | import com.android.dx.dex.DexOptions;
26 | import com.android.dx.dex.cf.CfOptions;
27 | import com.android.dx.dex.cf.CfTranslator;
28 | import com.android.dx.dex.file.DexFile;
29 | import com.android.dx.merge.CollisionPolicy;
30 | import com.android.dx.merge.DexMerger;
31 |
32 | import org.mozilla.javascript.GeneratedClassLoader;
33 |
34 | import java.io.IOException;
35 |
36 | /**
37 | * Compiles java bytecode to dex bytecode and loads it
38 | *
39 | * @author F43nd1r
40 | * @since 11.01.2016
41 | */
42 | abstract class BaseAndroidClassLoader extends ClassLoader implements GeneratedClassLoader {
43 |
44 | /**
45 | * Create a new instance with the given parent classloader
46 | *
47 | * @param parent the parent
48 | */
49 | public BaseAndroidClassLoader(ClassLoader parent) {
50 | super(parent);
51 | }
52 |
53 | /**
54 | * {@inheritDoc}
55 | */
56 | @Override
57 | public Class> defineClass(String name, byte[] data) {
58 | try {
59 | DexOptions dexOptions = new DexOptions();
60 | DexFile dexFile = new DexFile(dexOptions);
61 | DirectClassFile classFile = new DirectClassFile(data, name.replace('.', '/') + ".class", true);
62 | classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
63 | classFile.getMagic();
64 | dexFile.add(CfTranslator.translate(classFile, null, new CfOptions(), dexOptions, dexFile));
65 | Dex dex = new Dex(dexFile.toDex(null, false));
66 | Dex oldDex = getLastDex();
67 | if (oldDex != null) {
68 | dex = new DexMerger(new Dex[]{dex, oldDex}, CollisionPolicy.KEEP_FIRST).merge();
69 | }
70 | return loadClass(dex, name);
71 | } catch (IOException | ClassNotFoundException e) {
72 | throw new FatalLoadingException(e);
73 | }
74 | }
75 |
76 | protected abstract Class> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException;
77 |
78 | @Nullable
79 | protected abstract Dex getLastDex();
80 |
81 | protected abstract void reset();
82 |
83 | /**
84 | * Does nothing
85 | *
86 | * @param aClass ignored
87 | */
88 | @Override
89 | public void linkClass(Class> aClass) {
90 | //doesn't make sense on android
91 | }
92 |
93 | /**
94 | * Try to load a class. This will search all defined classes, all loaded jars and the parent class loader.
95 | *
96 | * @param name the name of the class to load
97 | * @param resolve ignored
98 | * @return the class
99 | * @throws ClassNotFoundException if the class could not be found in any of the locations
100 | */
101 | @Override
102 | public Class> loadClass(String name, boolean resolve)
103 | throws ClassNotFoundException {
104 | Class> loadedClass = findLoadedClass(name);
105 | if (loadedClass == null) {
106 | Dex dex = getLastDex();
107 | if (dex != null) {
108 | loadedClass = loadClass(dex, name);
109 | }
110 | if (loadedClass == null) {
111 | loadedClass = getParent().loadClass(name);
112 | }
113 | }
114 | return loadedClass;
115 | }
116 |
117 | /**
118 | * Might be thrown in any Rhino method that loads bytecode if the loading failed
119 | */
120 | public static class FatalLoadingException extends RuntimeException {
121 | FatalLoadingException(Throwable t) {
122 | super("Failed to define class", t);
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/FileAndroidClassLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Lukas Morawietz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.vove7.rhino.common;
18 |
19 |
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.Nullable;
22 |
23 | import com.android.dex.Dex;
24 |
25 | import java.io.File;
26 | import java.io.IOException;
27 |
28 | import dalvik.system.PathClassLoader;
29 |
30 | /**
31 | * @author F43nd1r
32 | * @since 24.10.2017
33 | */
34 | @SuppressWarnings("ResultOfMethodCallIgnored")
35 | class FileAndroidClassLoader extends BaseAndroidClassLoader {
36 | private static int instanceCounter = 0;
37 | private final File dexFile;
38 |
39 | /**
40 | * Create a new instance with the given parent classloader
41 | *
42 | * @param parent the parent
43 | */
44 | public FileAndroidClassLoader(ClassLoader parent, File cacheDir) {
45 | super(parent);
46 | int id = instanceCounter++;
47 | dexFile = new File(cacheDir, id + ".dex");
48 | cacheDir.mkdirs();
49 | reset();
50 | }
51 |
52 | @Override
53 | protected Class> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException {
54 | try {
55 | dex.writeTo(dexFile);
56 | } catch (IOException e) {
57 | e.printStackTrace();
58 | }
59 | return new PathClassLoader(dexFile.getPath(), getParent()).loadClass(name);
60 | }
61 |
62 | @Nullable
63 | @Override
64 | protected Dex getLastDex() {
65 | if (dexFile.exists()) {
66 | try {
67 | return new Dex(dexFile);
68 | } catch (IOException e) {
69 | e.printStackTrace();
70 | }
71 | }
72 | return null;
73 | }
74 |
75 | @Override
76 | protected void reset() {
77 | dexFile.delete();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/GcCollector.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.common;
2 |
3 | import org.mozilla.javascript.Scriptable;
4 | import org.mozilla.javascript.annotations.JSFunction;
5 |
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 | import java.util.List;
9 |
10 | /**
11 | * Created by 17719247306.
12 | * Date: 2018/8/26
13 | */
14 | public class GcCollector {
15 |
16 | public static final GcCollector Ins = new GcCollector();
17 |
18 | private static HashMap colls = new HashMap<>();
19 |
20 | static class RunData {
21 | Thread oriThread;
22 | List gcs = new ArrayList<>();
23 |
24 | public RunData(Thread oriThread) {
25 | this.oriThread = oriThread;
26 | }
27 | }
28 |
29 | public static void regMainThread(Scriptable scope, Thread thread) {
30 | RunData data = new RunData(thread);
31 | colls.put(scope, data);
32 | }
33 |
34 | @JSFunction
35 | public static void reg(Scriptable scope, Object o) {
36 | if (colls.containsKey(scope)) {
37 | colls.get(scope).gcs.add(o);
38 | }
39 | }
40 |
41 | public static void gc(Scriptable scope) {
42 | RunData data = colls.get(scope);
43 | if (data == null) {
44 | System.out.println("no data");
45 | return;
46 | }
47 | colls.remove(scope);
48 |
49 | List gcs = data.gcs;
50 | if (gcs != null) {
51 | for (Object gc : gcs) {
52 | if (gc instanceof Thread && ((Thread) gc).isAlive()) {
53 | ((Thread) gc).interrupt();
54 | }
55 | }
56 | gcs.clear();
57 | }
58 | Thread or = data.oriThread;
59 | if (or != null) {
60 | or.interrupt();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/InMemoryAndroidClassLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2017 Lukas Morawietz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package cn.vove7.rhino.common;
18 |
19 | import android.os.Build;
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.Nullable;
22 | import android.support.annotation.RequiresApi;
23 |
24 | import com.android.dex.Dex;
25 |
26 | import java.nio.ByteBuffer;
27 |
28 | import dalvik.system.InMemoryDexClassLoader;
29 |
30 | /**
31 | * @author F43nd1r
32 | * @since 24.10.2017
33 | */
34 |
35 | @RequiresApi(api = Build.VERSION_CODES.O)
36 | class InMemoryAndroidClassLoader extends BaseAndroidClassLoader {
37 | @Nullable
38 | private Dex last;
39 |
40 | public InMemoryAndroidClassLoader(ClassLoader parent) {
41 | super(parent);
42 | }
43 |
44 | @Override
45 | protected Class> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException {
46 | last = dex;
47 | return new InMemoryDexClassLoader(ByteBuffer.wrap(dex.getBytes()), getParent()).loadClass(name);
48 | }
49 |
50 | @Nullable
51 | @Override
52 | protected Dex getLastDex() {
53 | return last;
54 | }
55 |
56 | @Override
57 | protected void reset() {
58 | last = null;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/MapScriptable.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.common;
2 |
3 | import org.mozilla.javascript.Scriptable;
4 |
5 | import java.util.Collection;
6 | import java.util.Map;
7 | import java.util.Set;
8 |
9 | /**
10 | * 支持 map['key']
11 | *
12 | * [https://stackoverflow.com/questions/7519399/how-to-convert-java-map-to-a-basic-javascript-object]
13 | */
14 | public class MapScriptable implements Scriptable, Map {
15 | public final Map map;
16 |
17 | public MapScriptable(Map map) {
18 | this.map = map;
19 | }
20 |
21 | public void clear() {
22 | map.clear();
23 | }
24 |
25 | public boolean containsKey(Object key) {
26 | return map.containsKey(key);
27 | }
28 |
29 | public boolean containsValue(Object value) {
30 | return map.containsValue(value);
31 | }
32 |
33 | public Set entrySet() {
34 | return map.entrySet();
35 | }
36 |
37 | public boolean equals(Object o) {
38 | return map.equals(o);
39 | }
40 |
41 | public Object get(Object key) {
42 | return map.get(key);
43 | }
44 |
45 | public int hashCode() {
46 | return map.hashCode();
47 | }
48 |
49 | public boolean isEmpty() {
50 | return map.isEmpty();
51 | }
52 |
53 | public Set keySet() {
54 | return map.keySet();
55 | }
56 |
57 | public Object put(Object key, Object value) {
58 | return map.put(key, value);
59 | }
60 |
61 | public void putAll(Map m) {
62 | map.putAll(m);
63 | }
64 |
65 | public Object remove(Object key) {
66 | return map.remove(key);
67 | }
68 |
69 | public int size() {
70 | return map.size();
71 | }
72 |
73 | public Collection values() {
74 | return map.values();
75 | }
76 |
77 | @Override
78 | public void delete(String name) {
79 | map.remove(name);
80 | }
81 |
82 | @Override
83 | public void delete(int index) {
84 | map.remove(index);
85 | }
86 |
87 | @Override
88 | public Object get(String name, Scriptable start) {
89 | return map.get(name);
90 | }
91 |
92 | @Override
93 | public Object get(int index, Scriptable start) {
94 | return map.get(index);
95 | }
96 |
97 | @Override
98 | public String getClassName() {
99 | return map.getClass().getName();
100 | }
101 |
102 | @Override
103 | public Object getDefaultValue(Class> hint) {
104 | return toString();
105 | }
106 |
107 | @Override
108 | public Object[] getIds() {
109 | Object[] res = new Object[map.size()];
110 | int i = 0;
111 | for (Object k : map.keySet()) {
112 | res[i] = k;
113 | i++;
114 | }
115 | return res;
116 | }
117 |
118 | @Override
119 | public Scriptable getParentScope() {
120 | return null;
121 | }
122 |
123 | @Override
124 | public Scriptable getPrototype() {
125 | return null;
126 | }
127 |
128 | @Override
129 | public boolean has(String name, Scriptable start) {
130 | return map.containsKey(name);
131 | }
132 |
133 | @Override
134 | public boolean has(int index, Scriptable start) {
135 | return map.containsKey(index);
136 | }
137 |
138 | @Override
139 | public boolean hasInstance(Scriptable instance) {
140 | return false;
141 | }
142 |
143 | @Override
144 | public void put(String name, Scriptable start, Object value) {
145 | map.put(name, value);
146 | }
147 |
148 | @Override
149 | public void put(int index, Scriptable start, Object value) {
150 | map.put(index, value);
151 | }
152 |
153 | @Override
154 | public void setParentScope(Scriptable parent) {
155 | }
156 |
157 | @Override
158 | public void setPrototype(Scriptable prototype) {
159 | }
160 |
161 | @Override
162 | public String toString() {
163 | return map.toString();
164 | }
165 | }
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/NoSecurityController.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.common;
2 |
3 | import org.mozilla.javascript.Context;
4 | import org.mozilla.javascript.GeneratedClassLoader;
5 | import org.mozilla.javascript.SecurityController;
6 |
7 | import java.io.Serializable;
8 |
9 | /**
10 | * Created by Stardust on 2017/4/5.
11 | */
12 |
13 | public class NoSecurityController extends SecurityController implements Serializable {
14 | @Override
15 | public GeneratedClassLoader createClassLoader(ClassLoader classLoader, Object o) {
16 | return Context.getCurrentContext().createClassLoader(classLoader);
17 | }
18 |
19 | @Override
20 | public Object getDynamicSecurityDomain(Object o) {
21 | return null;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/rhino-android/src/main/java/cn/vove7/rhino/common/RhinoAndroidHelper.java:
--------------------------------------------------------------------------------
1 | package cn.vove7.rhino.common;
2 |
3 |
4 | import org.mozilla.javascript.Context;
5 | import org.mozilla.javascript.ContextFactory;
6 | import org.mozilla.javascript.SecurityController;
7 |
8 | import java.io.File;
9 |
10 | /**
11 | * Created by Stardust on 2017/4/5.
12 | */
13 |
14 | public class RhinoAndroidHelper {
15 |
16 | private final File cacheDirectory;
17 |
18 | /**
19 | * Constructs a new helper using the default temporary directory.
20 | * Note: It is recommended to use a custom directory, so no permission problems occur.
21 | */
22 | public RhinoAndroidHelper() {
23 | this(new File(System.getProperty("java.io.tmpdir", "."), "classes"));
24 | }
25 |
26 | /**
27 | * Constructs a new helper using a directory in the applications cache.
28 | *
29 | * @param context any context
30 | */
31 | public RhinoAndroidHelper(android.content.Context context) {
32 | this(new File(context.getCacheDir(), "classes"));
33 | }
34 |
35 | /**
36 | * Constructs a helper using the specified directory as cache.
37 | *
38 | * @param cacheDirectory the cache directory to use
39 | */
40 | public RhinoAndroidHelper(File cacheDirectory) {
41 | this.cacheDirectory = cacheDirectory;
42 | }
43 |
44 | /**
45 | * call this instead of {@link Context#enter()}
46 | *
47 | * @return a context prepared for android
48 | */
49 | public Context enterContext() {
50 | if (!SecurityController.hasGlobal())
51 | SecurityController.initGlobal(new NoSecurityController());
52 | return getContextFactory().enterContext();
53 | }
54 |
55 | /**
56 | * @return The Context factory which has to be used on android.
57 | */
58 | //@VisibleForTesting
59 | public AndroidContextFactory getContextFactory() {
60 | AndroidContextFactory factory;
61 | if (!ContextFactory.hasExplicitGlobal()) {
62 | factory = new AndroidContextFactory(cacheDirectory);
63 | ContextFactory.getGlobalSetter().setContextFactoryGlobal(factory);
64 | } else if (!(ContextFactory.getGlobal() instanceof AndroidContextFactory)) {
65 | throw new IllegalStateException("Cannot initialize factory for Android Rhino: There is already another factory");
66 | } else {
67 | factory = (AndroidContextFactory) ContextFactory.getGlobal();
68 | }
69 | return factory;
70 | }
71 |
72 | /**
73 | * @return a context prepared for android
74 | * @deprecated use {@link #enterContext()} instead
75 | */
76 | @Deprecated
77 | public static Context prepareContext() {
78 | return new RhinoAndroidHelper().enterContext();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':rhino-android'
--------------------------------------------------------------------------------