The origin of the story: Ratel, Repackaging Sandbox
At that time, I was thinking that VirtualXposed could achieve sandbox environment + god mode hooking capability based on VA.
I think many people must have thought of this, but only virjar has achieved it, which might be what makes him a big shot.
For better understanding, I think we can understand PingTouGe as the PRO version of Taichi, with more powerful features, a better ecosystem, and more suitable for our use.
Please note that such tools are all no root required, achieving the effect of running in a sandbox by repackaging the target app.
Related files in the article: https://github.com/runtimed/useRatel
Ratel Manager#
First, we need to compile a Ratel Manager, RM is the core of PingTouGe, which can be understood as the main program of Taichi and application reincarnation, used for secondary packaging of target apps, providing sandbox environment, Xposed plugin management, etc.
I couldn't find a finished product in the boss's repo, so I guess I have to compile it myself.
RM construction requires using a script to build; otherwise, it won't support repackaging on the app side.
git clone https://github.com/virjarRatel/ratel-core
cd ratel-core
# Start compiling
./script/build_ratel_manager.sh
Next comes the error handling time!
Connection refused
IO exception while downloading manifest:
java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200)
at java.net.Socket.connect(Socket.java:606)
at java.net.Socket.connect(Socket.java:555)
at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
at sun.net.www.http.HttpClient$1.run(HttpClient.java:515)
at sun.net.www.http.HttpClient$1.run(HttpClient.java:513)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.http.HttpClient.privilegedOpenServer(HttpClient.java:512)
Proxy issue, I previously set a proxy for gradle, now I remove it (because I installed a soft router at home).
## Remove proxy-related parts
vim .gradle/gradle.properties
During the troubleshooting process, I found that most issues were related to NDK.
Don't forget to add ANDROID_HOME to the environment variables, on my Mac it looks like this:
export ANDROID_HOME="/Users/{UserName}/Library/Android/sdk/"
In the end, I successfully compiled using ndk 21.1.6352462
.
Hope everyone compiles smoothly:
Basic Usage#
Using the target of the shell removal experiment "Online State Grid" for demonstration (not used before, just because it has Anti Root and is packed).
1. Infect the target application#
Infecting the target means repackaging the target, which runs in a sandbox environment.
In the infectable list, select:
There is also some use here; gda cannot recognize the package name of this apk, so you can directly look here as well.
After selection, it will automatically repackage.
Infection successful:
First uninstall the previously installed original apk, then install the infected app.
2. Create a new plugin#
https://github.com/virjarRatel/ratel-module-template is the plugin module template of PingTouGe, used to quickly generate a plugin project.
Supports two ways to generate plugins:
./template.sh -a ~/Downloads/wsgw.apk -m crack-wsgw # -a points to the target apk (will automatically recognize its package name)
./template.sh -p com.sgcc.wsgw.cn -m crack-wsgw # -p package name
Open with AS, if the plugin is green, it means no error:
This plugin structure is similar to Xposed modules, making it easy to get started:
For those unfamiliar with Xposed, you can check out the article I wrote when I was younger https://www.freebuf.com/articles/terminal/248675.html.
Shell Removal#
This target app is packed and has anti-root; it can barely run with Magisk Hide enabled.
So using frida-dexdump is quite difficult...
Direct decompilation shows that it is packed:
Next, we need to remove the shell; the shell removal plugin code:
public class HookEntry implements IRposedHookLoadPackage {
private static final String tag = "DEMO_HOOK";
@Override
public void handleLoadPackage(final RC_LoadPackage.LoadPackageParam lpparam) {
if (lpparam.packageName.equals("com.sgcc.wsgw.cn")) {
Unpack.entry(lpparam);
}
}
}
public class Unpack {
public static final String tag = "TR_HOOK";
public static void entry(RC_LoadPackage.LoadPackageParam lpparam) {
// TODO Enable this option to extract the shell
UnPackerToolKit.autoEnable(true);
UnPackerToolKit.unpack(new UnPackerToolKit.UnpackEvent() {
@Override
public void onFinish(File file) {
try {
FileUtils.copyFile(file, new File("/sdcard/wsgw-unpack.apk")); // apk generated after shell removal
} catch (IOException e) {
Log.e(tag, "unpacked error", e);
e.printStackTrace();
}
}
});
}
}
After editing the code, click the run button:
Enter adb logcat -s unpack
in the terminal to check the shell removal status:
Confirm the module is enabled:
Click on the target app in the infected application -> Force kill the process, then rerun the target app.
During the shell removal process, the constructUnpackedApk step always reports an error:
However, in the end, it still generated an apk for me:
adb pull /sdcard/wsgw-unpack.apk ./
Drag it into GDA to open; to be honest, this shell removal effect is quite good...:
Packet Capture#
Using VPN for packet capture, it prompts certificate verification exception, which should be due to SSL pinning or mutual HTTPS authentication:
Ratel has a built-in socketMonitor (which appeared 3 years earlier than R0Capture, and even supported thread jump tracking in the early days to solve asynchronous issues)
Writing the packet capture plugin:
public class HookEntry implements IRposedHookLoadPackage {
private static final String tag = "DEMO_HOOK";
@Override
public void handleLoadPackage(final RC_LoadPackage.LoadPackageParam lpparam) {
if (lpparam.packageName.equals("com.sgcc.wsgw.cn")) {
//Unpack.entry(lpparam);
Monitor.entry(lpparam);
}
}
}
public class Monitor {
public static void entry(RC_LoadPackage.LoadPackageParam lpparam) {
SocketMonitor.setPacketEventObserver(new FileLogEventObserver(new File(RatelToolKit.whiteSdcardDirPath, "socketMonitor")));
// The corresponding path is /sdcard/ratel_white_dir/com.sgcc.wsgw.cn/socketMonitor
}
}
After the plugin is installed, execute the functionality we need to capture packets, and then pull it down:
adb pull /sdcard/ratel_white_dir/com.sgcc.wsgw.cn/socketMonitor ./
It looks good; requests, responses, call stacks are all there, with each file corresponding to a request:
OS: The creation date of the file may help determine the request.
Warning: It has not been tested whether it handles native... Also, it has not been tested whether it supports certificate export; the documentation is quite sparse.
Reading Private Files of Target App#
This need often arises, and I personally think all no-root solutions should consider this issue. Let's see how Ratel does it.
Repo address: https://github.com/virjarRatel/sshdroid
Documentation can be found in the Readme and http://ratel-doc.virjar.com/04_ecology/SSHdroid.html:
git clone https://github.com/virjarRatel/sshdroid.git
cd sshdroid
cp app/src/main/assets/config.template.properties app/src/main/assets/config.properties
Open with AS and write the configuration file; after configuring, run directly:
Enable this module in RM's modules, then start the target app:
# Port forwarding
adb forward tcp:3478 tcp:3478
# SSH login
ssh 127.0.0.1 -p 3478
The current shell command interaction display has not been optimized.
Indeed, it hasn't been optimized, but it works.
TIPS: Just press enter for the password.
SCP is also supported, and the experience is quite good.
- Get a folder:
- Get a single file:
Cat is also supported:
Other commands will be discussed when needed; it is indeed very unhuman-friendly, but this shell is very suitable for automation scripts, with no extra information and no need to consider auto-completion.
Hook#
Randomly find a function to hook; find a function in the call stack from the previous packet capture, net.security.device.api.SecurityUtil.httpPost
:
Write a plugin to hook this function:
@Override
public void handleLoadPackage(final RC_LoadPackage.LoadPackageParam lpparam) {
Log.i(tag, "start");
// The API provided by Ratel automatically selects the classloader of the target class
ClassLoadMonitor.addClassLoadMonitor("net.security.device.api.SecurityUtil", new ClassLoadMonitor.OnClassLoader() {
@Override
public void onClassLoad(Class<?> clazz) {
Log.i(tag, "find class: " + clazz.getName());
RposedHelpers.findAndHookMethod(clazz, "httpPost",
String.class,
Map.class,
Map.class,
new RC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String arg1 = (String) param.args[0];
String arg2 = (String) param.args[1].toString();
String arg3 = (String) param.args[2].toString();
Log.i(tag, "args1:" + arg1);
Log.i(tag, "args2:" + arg2);
Log.i(tag, "args3:" + arg3);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
String res = (String) param.getResult();
Log.i(tag, "result:" + res);
Log.i(tag, "-------------------------");
}
});
}
});
Log.i(tag, "end");
}
Check the hook effect:
I haven't tested hooking native; I don't know how.
Postscript#
Ratel is quite powerful, but of course, it is not a silver bullet:
But it is already quite good, and you can consider secondary development to cope with the upcoming Anti.
Reference articles: