Skip to content

Commit

Permalink
增加了内存编辑与Dump功能。
Browse files Browse the repository at this point in the history
可以使用XServer进行内存读取/修改。
用于脱简单壳以及获取运行时SO文件。
  • Loading branch information
Monkeylord committed Jun 4, 2019
1 parent edfe73e commit 6505455
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 5 deletions.
10 changes: 8 additions & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified app/app-release.apk
Binary file not shown.
9 changes: 6 additions & 3 deletions app/src/main/assets/pages/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h2>Tips</h2>
<p>It can be used to locate/bypass network traffic encryption/sign.</p>
<p>Some Application may have several processes.To monitor specified process, you can use <i>'adb
forward tcp:8000 tcp:[pid]'</i> instead of 'adb forward tcp:8000 tcp:8000'</p>
<h1>Request Infomations:</h1>
<h2>Request Infomations:</h2>
<details>
<summary>Available Operations:</summary>
<#list opList as op>
Expand Down Expand Up @@ -56,12 +56,15 @@ <h1>Request Infomations:</h1>
</#list>
</details>
</#if>
<h1>Known Classes:</h1>
<h1>Memory</h1>
<p><a href="/memory">Memory Editor</a> allows you read/write/dump application's memory.</p>
<p>It can be used to extract .dex or .so file in the runtime.</p>
<h1>View/Monitor Known Classes:</h1>
<form action=/tracer method="post" target="_blank">
<p>
Filter:<input id="text" type="text" name="filter" onchange="refresh()"/>
</p>
<input type=submit value='Begin MassMonitoring(may not stable)'>
<input type=submit value='Begin MassMonitoring\r\n(may not stable if provided no parameters)'>
</form>
<div id=classes>
</div>
Expand Down
52 changes: 52 additions & 0 deletions app/src/main/assets/pages/memory.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<title>MemoryReader</title>
</head>
<body>
<details>
<summary>Memory Maps</summary>
<#list maps as map>
<p>${map}</p>
</#list>
</details>
<h1>Memory Editor:</h1>
<p>Address:<input id="addr" type="text"/></p>
<p>Count :<input id="count" type="text"/></p>

<input id="read" type="button" value="Read" onclick="readMem()"/>
<input id="write" type="button" value="Write" onclick="writeMem()"/>
<input id="dump" type="button" value="Dump" onclick="dumpMem()"/>
<p></p>
<textarea id="mem" rows="8" cols="32"></textarea>
</body>
<script type="text/javascript">
function readMem(){
var addr = parseInt(document.getElementById("addr").value, 16);
var count = parseInt(document.getElementById("count").value);
xml = new XMLHttpRequest();
xml.open("GET","/memory?op=read&addr="+addr+"&count="+count, false);
xml.send();
document.getElementById("mem").value=xml.responseText;
}
function dumpMem(){
var addr = parseInt(document.getElementById("addr").value, 16);
var count = parseInt(document.getElementById("count").value);
xml = new XMLHttpRequest();
xml.open("GET","/memory?op=dump&addr="+addr+"&count="+count, false);
xml.send();
document.getElementById("mem").value=xml.responseText;
}

// Does not work.
function writeMem(){
var addr = parseInt(document.getElementById("addr").value, 16);
var count = parseInt(document.getElementById("count").value);
xml = new XMLHttpRequest();
xml.open("POST","/memory?op=write&addr="+addr, false);
xml.send(document.getElementById("mem").value);
document.getElementById("mem").value=xml.responseText;
}

</script>
</html>
2 changes: 2 additions & 0 deletions app/src/main/java/monkeylord/XServer/XServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -60,6 +61,7 @@ public XServer(int port, Hashtable<String, Operation> 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);
Expand Down
91 changes: 91 additions & 0 deletions app/src/main/java/monkeylord/XServer/api/MemoryView.java
Original file line number Diff line number Diff line change
@@ -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<String, String> parms, Map<String, String> headers, Map<String, String> 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<String, Object> 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);
}
}
69 changes: 69 additions & 0 deletions app/src/main/java/monkeylord/XServer/handler/MemoryHandler.java
Original file line number Diff line number Diff line change
@@ -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<String, Method> 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");
}

}

0 comments on commit 6505455

Please sign in to comment.