Skip to content

Commit

Permalink
优化并提高稳定性
Browse files Browse the repository at this point in the history
Improved stability.
  • Loading branch information
Monkeylord committed Jul 26, 2019
1 parent a8be482 commit d0f8c00
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 24 deletions.
52 changes: 37 additions & 15 deletions app/src/main/assets/pages/tracer2.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@
Method Filter(RegEx):<input id="methodFilter" type="text" value=".*"/>
Thread Filter:<input id="threadFilter" type="text" value="0"/>
-->
<button id="clear" onclick="stop()">Clear Logs</button>
<button id="clear" @click="clearLog">Clear Logs</button>
<button id="loadMethods" onclick="getMths()" :disabled="allClzs.length==0">Load All Methods</button>
<button id="pause" onclick="pause()" disabled=true>Pause</button>
<a id="statistic">Resolved Classes: {{resolvedClzs.length}}/{{allClzs.length}} | Methods Found: {{(Object.keys(allMths).length)}} | Hooked Methods: {{(hookedMths.length==0)? "None/Unknown" : hookedMths.length}}</a>
<a id="statistic">WebSocket: {{(websocket)}} | Resolved Classes: {{resolvedClzs.length}}/{{allClzs.length}} | Methods Found: {{(Object.keys(allMths).length)}} | Hooked Methods: {{(hookedMths.length==0)? "None/Unknown" : hookedMths.length}}</a>
<br/>
<details open>
<summary id="classTree">Method Trace Selector <button id="redraw" onclick='genTree($("#classes"))' style='display: none;'>Redraw Classes Tree</button></summary>
<summary id="classTree">Method Trace Selector</summary>
Class Filter(RegEx):<input id="classFilter" type="text" v-model="clzFilter"/>
Method Filter(RegEx):<input id="methodFilter" type="text" v-model="mtdFilter"/>
<button id="loadMethod" @click="loadMethod">Load Methods for Matched Classes</button>
<button id="hook" @click="hookAll">Hook All Known Matched</button>
<button id="unhook" @click="unhookAll">Unhook All Known Matched</button>
<input type="checkbox" v-model:checked="renderClassTree"><label>Display Class Tree</label>
<button id="hookClz" @click="hookClz">Hook Matched Class</button>
<button id="hook" @click="hookAll">Hook Matched Methods</button>
<button id="unhook" @click="unhookAll">Unhook Matched Methods</button>
<input type="checkbox" v-model:checked="renderClassTree"><label>Class Tree</label>
<p>Matched Classes:{{selectedClzs.length}} | Matched Methods:{{selectedMtds.length}}</p>
<p v-if="loading!=0">Loading...({{loading}})</p>
<ul id="classes" style="overflow-y: scroll;overflow-x: hidden;max-height: 600px;">
Expand All @@ -53,10 +54,10 @@
</div>

<template id="classTreeTemplate">
<ul>
<a>{{scope.split(".").pop()}}</a>
<classtree v-for="(clzs,scp) in scopedClasses" :scope="scp" :classes="clzs"></classtree>
<p v-for="method in methods()" style="color: green;">
<ul style="padding-left: 20px;border-left: 1px outset;">
<a @click="fold">[{{(isUnfold)?"-":"+"}}]{{(scope=="")?"Application Package":scope.split(".").pop()}}</a>
<classtree v-if="isUnfold" v-for="(clzs,scp) in scopedClasses" :scope="scp" :classes="clzs"></classtree>
<p v-if="isUnfold" v-for="method in methods()" style="color: green;">
<input type="checkbox" class="checkbox" :checked="methodHooked(method)" :id="method" @click="doHook"/>
{{method}}
</p>
Expand Down Expand Up @@ -101,7 +102,8 @@
clzMths: {},
hookedMths: [],
loading: 0,
renderClassTree: true
renderClassTree: true,
websocket: "Not Connected"
}

var hookMessage = {
Expand Down Expand Up @@ -130,6 +132,7 @@
var classtreeCmp = Vue.component('classtree', {
// 声明 props
props: ['scope','classes'],
data:()=>{return {isUnfold:true}},
// 同样也可以在 vm 实例中像 "this.message" 这样使用
template: document.getElementById("classTreeTemplate"),
computed:{
Expand Down Expand Up @@ -171,6 +174,9 @@
// hook
doHookMethods([method])
}
},fold: function(){
this.isUnfold = !this.isUnfold
console.log(this.scope)
}
}
})
Expand All @@ -184,7 +190,14 @@
data: controlData,
computed: {
selectedClzs: function(){
return this.allClzs.filter(clz=>clz.class.match(this.clzFilter))
var filteredClzs = this.allClzs.filter(clz=>clz.class.match(this.clzFilter))
if(this.mtdFilter!="" && this.mtdFilter != ".*"){
var nonEmptyClzs = this.selectedMtds.map(method=>{
return method.split(";->")[0].slice(1)
})
filteredClzs = filteredClzs.filter(clz=>nonEmptyClzs.indexOf(clz.class)!=-1)
}
return filteredClzs
},
selectedMtds:function(){
return this.allMths.filter(method=>{
Expand Down Expand Up @@ -222,6 +235,12 @@
return clz.match(this.clzFilter) && mname.match(this.mtdFilter) && this.hookedMths.indexOf(method)!=-1
})
doUnhookMethods(hooked)
},
hookClz: function(){
doHookClasses(this.selectedClzs.map(clz=>clz.class))
},
clearLog: function(){
hookMessage.children = []
}
}
})
Expand Down Expand Up @@ -249,11 +268,14 @@
}

//连接发生错误的回调方法
websocket.onerror = (event)=>{alert("Connection Error!");console.log(event)}
websocket.onerror = (event)=>{
controlData.websocket = "Error"
alert("Connection Error!");console.log(event)
}

//连接成功建立的回调方法
websocket.onopen = ()=>{
divMsg.innerHTML = "WebSocket Connected!"
controlData.websocket = "Connected"
// 立刻请求类清单
getAllClzs()
doHookMethods([])
Expand All @@ -267,7 +289,7 @@

//连接关闭的回调方法
websocket.onclose = function () {
setMessageInnerHTML("WebSocket Closed");
controlData.websocket = "Closed"
document.getElementById('loadMethods').disabled=true;
document.getElementById('clear').disabled=true;
}
Expand Down
33 changes: 24 additions & 9 deletions app/src/main/java/monkeylord/XServer/api/wsTracerNew.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,43 @@ public void handleMessage(String msg){
JSONObject call = new JSONObject();
String[] methods ={};
String[] classes = {};
ArrayList<String> clzArr = new ArrayList<>();
ArrayList<String> methodArr = new ArrayList<>();
ArrayList<String> errArr = new ArrayList<>();
switch ((String) req.get("type")) {
case "hook":
methods = ((JSONArray) req.get("methods")).toArray(methods);
for (String mtd:methods) {
Method m = MethodHandler.getMethodbyJavaName(mtd);
if(!unhooks.containsKey(mtd))unhooks.put(mtd, XposedBridge.hookMethod(m, hook));
try {
Method m = MethodHandler.getMethodbyJavaName(mtd);
if (!unhooks.containsKey(mtd))
unhooks.put(mtd, XposedBridge.hookMethod(m, hook));
}catch (Throwable e){
e.printStackTrace();
errArr.add(mtd+":"+e.getMessage());
}
}
call.put("type","hook");
call.put("errors",errArr);
call.put("hooks",unhooks.keySet());
break;
case "hookClass":
classes = ((JSONArray) req.get("classes")).toArray(methods);
for (String clzname:classes) {
Class clz = Class.forName(clzname,false,XposedEntry.classLoader);
for (Method m:clz.getDeclaredMethods()) {
String mtd = Utils.getJavaName(m);
if(!unhooks.containsKey(mtd))unhooks.put(mtd, XposedBridge.hookMethod(m, hook));
try {
Class clz = Class.forName(clzname, false, XposedEntry.classLoader);
for (Method m : clz.getDeclaredMethods()) {
String mtd = Utils.getJavaName(m);
if (!unhooks.containsKey(mtd))
unhooks.put(mtd, XposedBridge.hookMethod(m, hook));
}
}catch (Throwable e){
e.printStackTrace();
errArr.add(clzname+":"+e.getMessage());
}
}
call.put("type","hook");
call.put("errors",errArr);
call.put("hooks",unhooks.keySet());
break;
case "unhook":
Expand All @@ -77,9 +94,6 @@ public void handleMessage(String msg){
call.put("classes",DexHelper.getClassesInDex(XposedEntry.classLoader));
break;
case "methods":
ArrayList<String> clzArr = new ArrayList<>();
ArrayList<String> methodArr = new ArrayList<>();
ArrayList<String> errArr = new ArrayList<>();
classes = ((JSONArray) req.get("classes")).toArray(classes);
for (String clz:classes) {
try{
Expand Down Expand Up @@ -133,6 +147,7 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
result.put("method",Utils.getJavaName((Method) param.method));
result.put("this", ObjectHandler.briefObject(param.thisObject));
result.put("result",ObjectHandler.briefObject(param.getResult()));
if(param.hasThrowable())result.put("throw",ObjectHandler.briefObject(param.getThrowable()));

websocket.trySend(result.toJSONString());
}
Expand Down

0 comments on commit d0f8c00

Please sign in to comment.