Code restoration technology: Unidbg hook_add_new to achieve conditional breakpoints (Part 2)
1. Objectives
When doing code restoration, sometimes you will analyze a set of results and hope to set a conditional breakpoint in the middle, such as triggering a breakpoint at code line 0x1234, R0=0x5678.
Let’s try it today.
Tip:
The Unidbg code has been synchronized to the latest official version, which already supports the display of floating-point registers.
2. Steps
Write a floatdemotwo first
Upgrade the ancestral algorithm
extern "C" JNIEXPORT jstring JNICALL
Java_com_h1yx_app_floatdemo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject Obj, jdouble value) {
std::string hello = "Hello from C++";
double p=3.14159;
double s,v,rc;
for(int i=0 ; i< 10; i++){
hello +="\n";
v = 2*p* (value + i);
s = p * (value + i) * (value + i);
rc = v+s;
hello += std::to_string(rc);
}
return env->NewStringUTF(hello.c_str());
}
Calculate the sum of the circumference and area of 10 circles.
The printed result is like this
Hello from C++
150.796320
197.920170
251.327200
311.017410
376.990800
449.247370
527.787120
612.610050
703.716160
801.105450
Our goal is to achieve results equal to449.247370When the breakpoint is triggered, pretend to analyze the following527.787120The calculation process.
IDA
We set a breakpoint at 0x127C0 and single-step a few times until we reach 0x127D4. After executing the addition instruction FADD D0, D1, D0 , we can see that the value of D0 is 150.796320 .
This is the target. We set the conditional breakpoint at 0x127D4, which is triggered when D0=449.247370.
hook_add_new
First create a ffcodehook class, inherit from com.github.unidbg.arm.backend.CodeHook and then add it to the emulator
// com/h1yx/test/runfloatdemo.java
analyseHookA = new FFCodehook(emulator);
emulator.getBackend().hook_add_new(analyseHookA, module.base + 0x127D8, module.base + 0x127D8, emulator);
// com/h1yx/test/ffcodehook.java
public class FFCodehook implements CodeHook {
private final Emulator<?> emulator;
public FFCodehook(Emulator<?> emulator) {
super();
this.emulator = emulator;
}
private Unicorn.UnHook unHook;
@Override
public void onAttach(Unicorn.UnHook unHook) {
if (this.unHook != null) {
throw new IllegalStateException();
}
this.unHook = unHook;
}
@Override
public void detach() {
if (unHook != null) {
unHook.unhook();
unHook = null;
}
}
private static BigInteger newBigInteger(byte[] data) {
if (data.length != 16) {
throw new IllegalStateException("data.length=" + data.length);
}
byte[] copy = Arrays.copyOf(data, data.length);
for (int i = 0; i < 8; i++) {
byte b = copy[i];
copy[i] = copy[15 - i];
copy[15 - i] = b;
}
byte[] bytes = new byte[copy.length + 1];
System.arraycopy(copy, 0, bytes, 1, copy.length); // makePositive
return new BigInteger(bytes);
}
@Override
public void hook(Backend backend, long address, int size, Object user) {
try {
if (address == 0x400127d8) {
byte[] data = backend.reg_read_vector(Arm64Const.UC_ARM64_REG_Q0);
if (data != null) {
String strShow = String.format(Locale.US, " Q0=0x%s%s", newBigInteger(data).toString(16), Utils.decodeVectorRegister(data));
System.out.println("##### value " + strShow);
}
}
} catch (BackendException e) {
throw new IllegalStateException(e);
}
}
}
In this way, the value of D0 when running to the 0x127D8 instruction can be printed out.
##### value Q0=0x4062d97b7414a4d2(150.79631999999998)
##### value Q0=0x4068bd72085b1854(197.92016999999998)
##### value Q0=0x406f6a786c22680a(251.3272)
##### value Q0=0x407370474fb549fa(311.01741000000004)
##### value Q0=0x40778fda5119ce07(376.9908)
##### value Q0=0x407c13f53a3ec02f(449.24737)
##### value Q0=0x40807e4c05921038(527.78712)
##### value Q0=0x408324e161e4f765(612.6100499999999)
##### value Q0=0x4085fdbab21815a0(703.71616)
##### value Q0=0x408908d7f62b6ae8(801.10545)
Come on a condition
Now we can make a judgment. When D0=449.247370, we will break on the next line and enter the debugging mode.
double bOutD = bytes2Double(data);
if(bOutD == 449.247370){
Debugger MyDbg = emulator.attach(DebuggerType.CONSOLE);
MyDbg.addBreakPoint(0x400127dc);
}
OK, we have successfully entered the debugging mode, and we can analyze the following527.787120The calculation process.
Note: Floating point numbers cannot be directly judged using == because the precision is different. A safer approach is as follows:
final double THRESHOLD = .0001;
double bOutD = bytes2Double(data);
if (Math.abs(bOutD - 449.247370 ) < THRESHOLD) {
...
}
Conclusion
Conditional breakpoints are useful when analyzing a set of data.
hook_add_new can actually be used as an Inline Hook. You may ask, isn’t it better to use xhook and some other Hook tools for Inline Hook?
Wukong, when you encounter those monsters with great powers that can detect whether important codes have been modified, you will remember the benefits of “hardware breakpoints”.