diff --git a/.idea/misc.xml b/.idea/misc.xml
index e0d5b93..51fa3e5 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -5,7 +5,7 @@
-
+
@@ -13,18 +13,24 @@
+
+
+
-
+
+
+
+
diff --git a/app/app-release.apk b/app/app-release.apk
index 72ec5d2..19a0c19 100644
Binary files a/app/app-release.apk and b/app/app-release.apk differ
diff --git a/app/src/main/assets/pages/index.html b/app/src/main/assets/pages/index.html
index 12dabd8..1ca5811 100644
--- a/app/src/main/assets/pages/index.html
+++ b/app/src/main/assets/pages/index.html
@@ -15,7 +15,7 @@
Tips
It can be used to locate/bypass network traffic encryption/sign.
Some Application may have several processes.To monitor specified process, you can use 'adb
forward tcp:8000 tcp:[pid]' instead of 'adb forward tcp:8000 tcp:8000'
-Request Infomations:
+Request Infomations:
Available Operations:
<#list opList as op>
@@ -56,12 +56,15 @@ Request Infomations:
#list>
#if>
-Known Classes:
+Memory
+Memory Editor allows you read/write/dump application's memory.
+It can be used to extract .dex or .so file in the runtime.
+View/Monitor Known Classes:
diff --git a/app/src/main/assets/pages/memory.html b/app/src/main/assets/pages/memory.html
new file mode 100644
index 0000000..622740c
--- /dev/null
+++ b/app/src/main/assets/pages/memory.html
@@ -0,0 +1,52 @@
+
+
+
+ MemoryReader
+
+
+
+ Memory Maps
+ <#list maps as map>
+ ${map}
+ #list>
+
+Memory Editor:
+Address:
+Count :
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/monkeylord/XServer/XServer.java b/app/src/main/java/monkeylord/XServer/XServer.java
index 696f05e..88aae6b 100644
--- a/app/src/main/java/monkeylord/XServer/XServer.java
+++ b/app/src/main/java/monkeylord/XServer/XServer.java
@@ -14,6 +14,7 @@
import monkeylord.XServer.api.ClassView;
import monkeylord.XServer.api.Invoke;
import monkeylord.XServer.api.Invoke_New;
+import monkeylord.XServer.api.MemoryView;
import monkeylord.XServer.api.MethodView;
import monkeylord.XServer.api.Tracer;
import monkeylord.XServer.api.wsMethodView;
@@ -60,6 +61,7 @@ public XServer(int port, Hashtable route) {
XServer.route.put("/tracer", new Tracer());
XServer.route.put("/invoke", new Invoke());
XServer.route.put("/invoke2", new Invoke_New());
+ XServer.route.put("/memory", new MemoryView());
try {
//启动监听
start(0, false);
diff --git a/app/src/main/java/monkeylord/XServer/api/MemoryView.java b/app/src/main/java/monkeylord/XServer/api/MemoryView.java
new file mode 100644
index 0000000..3ee6d61
--- /dev/null
+++ b/app/src/main/java/monkeylord/XServer/api/MemoryView.java
@@ -0,0 +1,91 @@
+package monkeylord.XServer.api;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import monkeylord.XServer.XServer;
+import monkeylord.XServer.handler.MemoryHandler;
+import monkeylord.XServer.handler.MethodHandler;
+
+public class MemoryView implements XServer.Operation {
+ @Override
+ public String handle(String url, Map parms, Map headers, Map files) {
+ try {
+ Method method=null;
+ if(parms.get("op")!=null) {
+ switch (parms.get("op")){
+ case "dump":
+ byte[] binary=MemoryHandler.readMemory(Long.parseLong(parms.get("addr")),Integer.parseInt(parms.get("count")));
+ FileOutputStream fo = new FileOutputStream(new File("/sdcard/XServer.dump"));
+ fo.write(binary);
+ return "Dumped at /sdcard/XServer.dump";
+ case "read":
+ byte[] bytes=MemoryHandler.readMemory(Long.parseLong(parms.get("addr")),Integer.parseInt(parms.get("count")));
+ return bytesToHexString(bytes);
+ case "write":
+ MemoryHandler.writeMemory(Long.parseLong(parms.get("addr")),hexStringToBytes(files.get("postData")));
+ byte[] bytes_res=MemoryHandler.readMemory(Long.parseLong(parms.get("addr")),hexStringToBytes(files.get("postData")).length);
+ return bytesToHexString(bytes_res);
+ default:
+ return "";
+ }
+ }else{
+ HashMap map = new HashMap<>();
+ map.put("maps", MemoryHandler.getMaps());
+ return XServer.render(map, "pages/memory.html");
+ }
+ } catch (Exception e) {
+ return e.getLocalizedMessage();
+ }
+ }
+
+ public static String bytesToHexString(byte[] src){
+ StringBuffer stringBuffer = new StringBuffer(src.length*2+2);
+ byte[] hexs = "0123456789ABCDEF".getBytes();
+ if (src == null || src.length <= 0) {
+ return null;
+ }
+ for (int i = 0; i < src.length; i++) {
+ //Log.e("XServer", "bytesToHexString: "+((src[i]&0xFF)>>4));
+ //Log.e("XServer", "bytesToHexString: "+((src[i]&0x0F)));
+ stringBuffer.append((char) (hexs[(src[i]&0xFF)>>4]));
+ stringBuffer.append((char) (hexs[src[i]&0x0F]));
+ /*
+ int v = src[i] & 0xFF;
+ String hv = Integer.toHexString(v);
+ if (hv.length() < 2) {
+ stringBuffer.append(0);
+ }
+ stringBuffer.append(hv);
+ */
+ }
+ return stringBuffer.toString();
+ }
+ public static byte[] hexStringToBytes(String hexString) {
+ if (hexString == null || hexString.equals("")) {
+ return null;
+ }
+ hexString = hexString.toUpperCase();
+ int length = hexString.length() / 2;
+ char[] hexChars = hexString.toCharArray();
+ byte[] d = new byte[length];
+ for (int i = 0; i < length; i++) {
+ int pos = i * 2;
+ d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+ }
+ return d;
+ }
+ /**
+ * Convert char to byte
+ * @param c char
+ * @return byte
+ */
+ private static byte charToByte(char c) {
+ return (byte) "0123456789ABCDEF".indexOf(c);
+ }
+}
diff --git a/app/src/main/java/monkeylord/XServer/handler/MemoryHandler.java b/app/src/main/java/monkeylord/XServer/handler/MemoryHandler.java
new file mode 100644
index 0000000..22c3b29
--- /dev/null
+++ b/app/src/main/java/monkeylord/XServer/handler/MemoryHandler.java
@@ -0,0 +1,69 @@
+package monkeylord.XServer.handler;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+import de.robv.android.xposed.XposedBridge;
+import monkeylord.XServer.XposedEntry;
+
+public class MemoryHandler {
+ static HashMap Memory = new HashMap<>();
+ static {
+ try {
+ // http://androidxref.com/4.4_r1/xref/libcore/luni/src/main/java/libcore/io/Memory.java
+ Class clzMemory = Class.forName("libcore.io.Memory");
+ for (Method m:clzMemory.getDeclaredMethods()) {
+ Memory.put(m.getName(),m);
+ }
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ public static byte[] readMemory(long address,int count) throws InvocationTargetException, IllegalAccessException {
+ if(Memory.get("peekByteArray")==null)throw new IllegalAccessException();
+ byte[] data=new byte[count];
+ Memory.get("peekByteArray").invoke(null,address, data, 0, count);
+ return data;
+ }
+ public static void writeMemory(long address,byte[] data) throws IllegalAccessException, InvocationTargetException {
+ if(Memory.get("pokeByteArray")==null)throw new IllegalAccessException();
+ byte[] mem = readMemory(address,data.length);
+ for (int i = 0; i < mem.length; i++) {
+ if(mem[i]!=data[i]){
+ Memory.get("pokeByte").invoke(null,address+i,data[i]);
+ }
+ }
+ //Memory.get("pokeByteArray").invoke(null,address, mem, 0, mem.length);
+ }
+ public static String[] getMaps(){
+ File maps=new File("/proc/self/maps");
+ StringBuilder sb = new StringBuilder();
+ try {
+ FileInputStream is = new FileInputStream(maps);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ String str;
+ while((str=reader.readLine())!=null){
+ sb.append(str+"\r\n");
+ }
+ /*
+ Process p = Runtime.getRuntime().exec("cat /proc/self/maps");
+ p.waitFor();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ String str;
+ while((str=reader.readLine())!=null){
+ sb.append(str+"\r\n");
+ }
+ */
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return sb.toString().split("\r\n");
+ }
+
+}