package
MyTool;
import
com.github.unidbg.Emulator;
import
com.github.unidbg.Module;
import
com.github.unidbg.Symbol;
import
com.github.unidbg.arm.backend.*;
import
org.apache.commons.codec.binary.Hex;
import
unicorn.Arm64Const;
import
java.io.FileNotFoundException;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.nio.ByteOrder;
import
java.util.HashMap;
import
java.util.Map;
/**
* @author: FANGG3
* @date: 2022/10/24 14:20
* @desc:
*/
public
class
AttdTracer {
Emulator emulator;
String out_path;
FileOutputStream log_stream;
long
base;
long
end;
long
init_sp;
static
int
memIndex =
0
;
boolean
isLog =
false
;
Module module;
boolean
isStart =
false
;
String pre_instr;
String mem_record;
long
pos =
0
;
StringBuilder record =
new
StringBuilder();
private
Map<Integer, String> REGS =
new
HashMap<Integer, String>() {{
put(Arm64Const.UC_ARM64_REG_X0,
"X0"
);
put(Arm64Const.UC_ARM64_REG_X1,
"X1"
);
put(Arm64Const.UC_ARM64_REG_X2,
"X2"
);
put(Arm64Const.UC_ARM64_REG_X3,
"X3"
);
put(Arm64Const.UC_ARM64_REG_X4,
"X4"
);
put(Arm64Const.UC_ARM64_REG_X5,
"X5"
);
put(Arm64Const.UC_ARM64_REG_X6,
"X6"
);
put(Arm64Const.UC_ARM64_REG_X7,
"X7"
);
put(Arm64Const.UC_ARM64_REG_X8,
"X8"
);
put(Arm64Const.UC_ARM64_REG_X9,
"X9"
);
put(Arm64Const.UC_ARM64_REG_X10,
"X10"
);
put(Arm64Const.UC_ARM64_REG_X11,
"X11"
);
put(Arm64Const.UC_ARM64_REG_X12,
"X12"
);
put(Arm64Const.UC_ARM64_REG_X13,
"X13"
);
put(Arm64Const.UC_ARM64_REG_X14,
"X14"
);
put(Arm64Const.UC_ARM64_REG_X15,
"X15"
);
put(Arm64Const.UC_ARM64_REG_X16,
"X16"
);
put(Arm64Const.UC_ARM64_REG_X17,
"X17"
);
put(Arm64Const.UC_ARM64_REG_X18,
"X18"
);
put(Arm64Const.UC_ARM64_REG_X19,
"X19"
);
put(Arm64Const.UC_ARM64_REG_X20,
"X20"
);
put(Arm64Const.UC_ARM64_REG_X21,
"X21"
);
put(Arm64Const.UC_ARM64_REG_X22,
"X22"
);
put(Arm64Const.UC_ARM64_REG_X23,
"X23"
);
put(Arm64Const.UC_ARM64_REG_X24,
"X24"
);
put(Arm64Const.UC_ARM64_REG_X25,
"X25"
);
put(Arm64Const.UC_ARM64_REG_X26,
"X26"
);
put(Arm64Const.UC_ARM64_REG_X27,
"X27"
);
put(Arm64Const.UC_ARM64_REG_X28,
"X28"
);
put(Arm64Const.UC_ARM64_REG_LR,
"X29"
);
put(Arm64Const.UC_ARM64_REG_FP,
"X30"
);
put(Arm64Const.UC_ARM64_REG_PC,
"PC"
);
put(Arm64Const.UC_ARM64_REG_NZCV,
"NZCV"
);
}};
public
AttdTracer(Emulator emulator, Module module, String out_path,
boolean
isLog) {
this
.emulator = emulator;
this
.out_path = out_path;
this
.isLog = isLog;
this
.module = module;
try
{
this
.log_stream =
new
FileOutputStream(
this
.out_path);
}
catch
(FileNotFoundException e) {
throw
new
RuntimeException(e);
}
init_sp = emulator.getContext().getStackPointer().peer;
if
(module ==
null
) {
base =
0
;
end = -
1
;
return
;
}
base = module.base;
end = base + module.size;
}
public
String getSymbolByAddr(
long
addr) {
try
{
Symbol symbol = emulator.getMemory().findModuleByAddress(addr).findClosestSymbolByAddress(addr,
false
);
long
offset = addr - symbol.getAddress();
if
(offset >=
0x1000
)
return
null
;
return
String.format(
"%s+%x"
, symbol, offset);
}
catch
(Exception e) {
return
null
;
}
}
public
String getRegs(Backend backend) {
StringBuilder ret =
new
StringBuilder();
REGS.forEach((id, s) -> {
long
v = backend.reg_read(id).longValue();
String symbol = getSymbolByAddr(v);
if
(symbol ==
null
) {
ret.append(String.format(
"%s=%x,"
,s, v));
}
else
{
ret.append(String.format(
"%s=%x:%s,"
, s,v, symbol));
}
});
return
ret.toString();
}
private
void
writeTrace(String data){
try
{
if
(
this
.isLog){
System.out.println(data);
}
this
.log_stream.write(data.getBytes());
this
.log_stream.write(
'\n'
);
}
catch
(IOException e) {
throw
new
RuntimeException(e);
}
}
public
void
trace() {
emulator.getBackend().hook_add_new(
new
CodeHook() {
@Override
public
void
hook(Backend backend,
long
address,
int
size, Object user) {
String instrValue = Hex.encodeHexString(backend.mem_read(address,
4
));
if
(!isStart) {
String symbolByAddr = getSymbolByAddr(address);
if
(symbolByAddr !=
null
) {
pre_instr = String.format(
"inst=%x:%s:%s,"
, address, instrValue, symbolByAddr);
}
else
{
pre_instr = String.format(
"inst=%x:%s,"
, address, instrValue);
}
isStart =
true
;
return
;
}
String now_reg = getRegs(backend);
record.append(pre_instr)
.append(now_reg)
.append(mem_record);
writeTrace(record.toString());
record =
new
StringBuilder();
pos +=
1
;
mem_record =
""
;
pre_instr = String.format(
"inst=%x:%s,"
, address, instrValue);
}
@Override
public
void
onAttach(UnHook unHook) {
}
@Override
public
void
detach() {
}
},
0
, -
1
,
null
);
emulator.getBackend().hook_add_new(
new
ReadHook() {
@Override
public
void
hook(Backend backend,
long
address,
int
size, Object user) {
Class c;
byte
[] bytes = backend.mem_read(address, size);
String v;
switch
(size) {
case
1
:
c = Byte.
class
;
v = String.valueOf(bytes[
0
] &
0xFF
);
break
;
case
2
:
c = Short.
class
;
Number number = bytesToNumber(bytes, c, ByteOrder.LITTLE_ENDIAN);
v = String.valueOf(Short.toUnsignedInt(number.shortValue()));
break
;
case
4
:
c = Integer.
class
;
int
i = bytesToNumber(bytes, c, ByteOrder.LITTLE_ENDIAN).intValue();
v = Integer.toUnsignedString(i);
break
;
case
8
:
c = Long.
class
;
long
L = bytesToNumber(bytes, c, ByteOrder.LITTLE_ENDIAN).longValue();
v = Long.toUnsignedString(L);
break
;
default
:
throw
new
IllegalStateException(
"convert mem bytes, size="
+ size);
}
mem_record += String.format(
"mr=%x:%s:%d,"
, address, v, size);
}
@Override
public
void
onAttach(UnHook unHook) {}
@Override
public
void
detach() {}
},
0
, -
1
,
null
);
emulator.getBackend().hook_add_new(
new
WriteHook() {
@Override
public
void
hook(Backend backend,
long
address,
int
size,
long
value, Object user) {
mem_record += String.format(
"mw=%x:%s:%d,"
, address, Long.toUnsignedString(value), size);
}
@Override
public
void
onAttach(UnHook unHook) {}
@Override
public
void
detach() {}
},
0
, -
1
,
null
);
}
public
static
short
bytesToShort(
byte
[] bytes,
int
start, ByteOrder byteOrder) {
return
ByteOrder.LITTLE_ENDIAN == byteOrder ? (
short
)(bytes[start] &
255
| (bytes[start +
1
] &
255
) <<
8
) : (
short
)(bytes[start +
1
] &
255
| (bytes[start] &
255
) <<
8
);
}
public
static
int
bytesToInt(
byte
[] bytes,
int
start, ByteOrder byteOrder) {
return
ByteOrder.LITTLE_ENDIAN == byteOrder ? bytes[start] &
255
| (bytes[
1
+ start] &
255
) <<
8
| (bytes[
2
+ start] &
255
) <<
16
| (bytes[
3
+ start] &
255
) <<
24
: bytes[
3
+ start] &
255
| (bytes[
2
+ start] &
255
) <<
8
| (bytes[
1
+ start] &
255
) <<
16
| (bytes[start] &
255
) <<
24
;
}
public
static
long
bytesToLong(
byte
[] bytes,
int
start, ByteOrder byteOrder) {
long
values = 0L;
int
i;
if
(ByteOrder.LITTLE_ENDIAN == byteOrder) {
for
(i =
7
; i >=
0
; --i) {
values <<=
8
;
values |= (
long
)(bytes[i + start] &
255
);
}
}
else
{
for
(i =
0
; i <
8
; ++i) {
values <<=
8
;
values |= (
long
)(bytes[i + start] &
255
);
}
}
return
values;
}
public
static
<T
extends
Number> T bytesToNumber(
byte
[] bytes, Class<T> targetClass, ByteOrder byteOrder)
throws
IllegalArgumentException {
Object number =
null
;
if
(Byte.
class
== targetClass) {
number = bytes[
0
];
}
else
if
(Short.
class
== targetClass) {
number = bytesToShort(bytes,
0
, byteOrder);
}
else
if
(Integer.
class
== targetClass) {
number = bytesToInt(bytes,
0
,byteOrder);
}
else
if
(Long.
class
== targetClass) {
number = bytesToLong(bytes,
0
,byteOrder);
}
else
{
if
(Number.
class
!= targetClass) {
throw
new
IllegalArgumentException(
"Unsupported Number type: "
+ targetClass.getName());
}
}
return
(T) number;
}
}