FartExt之优化更深主动调用的FART10
看雪论坛作者ID:misskings
需求
对指定进程脱壳,非目标进程不要执行脱壳线程。 对指定类列表进行脱壳。 将FART升级到aosp10实现。 去FART指纹。 FART保存出的函数修复合并为dex。 实现FART更深的主动调用。 更快的主动调用(暂未优化)。
1
什么是FART
还未了解过的请看原作者对于fart的介绍:
FART:ART环境下基于主动调用的自动化脱壳方案(https://bbs.pediy.com/thread-252630.htm)
FART正餐前甜点:ART下几个通用简单高效的dump内存中dex方法(https://bbs.pediy.com/thread-254028.htm)
拨云见日:安卓APP脱壳的本质以及如何快速发现ART下的脱壳点(https://bbs.pediy.com/thread-254555.htm)
fart的理解和分析过程(https://bbs.pediy.com/thread-263401.htm)
2
对指定进程脱壳
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
fartthread();
...
}
//判断这个进程是否应该脱壳
public static boolean shouldUnpack() {
boolean should_unpack = false;
String processName = ActivityThread.currentProcessName();
BufferedReader br = null;
String configPath="/data/local/tmp/fext.config";
try {
br = new BufferedReader(new FileReader(configPath));
String line;
while ((line = br.readLine()) != null) {
if (processName.equals(line))) {
should_unpack = true;
break;
}
}
br.close();
}
catch (Exception ignored) {
}
return should_unpack;
}
//启动FART脱壳线程
public static void fartthread() {
if (!shouldUnpack()) {
return;
}
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
Log.e("ActivityThread", "start sleep......");
Thread.sleep(1 * 60 * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.e("ActivityThread", "sleep over and start fart");
fart();
Log.e("ActivityThread", "fart run over");
}
}).start();
}
private void handleBindApplication(AppBindData data) {
...
app = data.info.makeApplication(data.restrictedBackupMode, null);
app.setAutofillOptions(data.autofillOptions);
app.setContentCaptureOptions(data.contentCaptureOptions);
mInitialApplication = app;
fartthread();
...
}
3
对指定类列表进行脱壳
//读取类列表
public static String getClassList() {
String processName = ActivityThread.currentProcessName();
BufferedReader br = null;
String configPath="/data/local/tmp/"+processName;
Log.e("ActivityThread", "getClassList processName:"+processName);
StringBuilder sb=new StringBuilder();
try {
br = new BufferedReader(new FileReader(configPath));
String line;
while ((line = br.readLine()) != null) {
if(line.length()>=2){
sb.append(line+"\n");
}
}
br.close();
}
catch (Exception ex) {
Log.e("ActivityThread", "getClassList err:"+ex.getMessage());
return "";
}
return sb.toString();
}
//对指定类进行主动调用
public static void fartWithClassList(String classlist){
ClassLoader appClassloader = getClassloader();
if(appClassloader==null){
Log.e("ActivityThread", "appClassloader is null");
return;
}
Class DexFileClazz = null;
try {
DexFileClazz = appClassloader.loadClass("dalvik.system.DexFile");
} catch (Exception e) {
e.printStackTrace();
} catch (Error e) {
e.printStackTrace();
}
Method dumpMethodCode_method = null;
for (Method field : DexFileClazz.getDeclaredMethods()) {
if (field.getName().equals("fartextMethodCode")) {
dumpMethodCode_method = field;
dumpMethodCode_method.setAccessible(true);
}
}
String[] classes=classlist.split("\n");
for(String clsname : classes){
String line=clsname;
if(line.startsWith("L")&&line.endsWith(";")&&line.contains("/")){
line=line.substring(1,line.length()-1);
line=line.replace("/",".");
}
loadClassAndInvoke(appClassloader, line, dumpMethodCode_method);
}
}
public static void fartthread() {
if (!shouldUnpack()) {
return;
}
//获取类列表。如果有的话就不要完整主动调用了
String classlist=getClassList();
if(!classlist.equals("")){
fartWithClassList(classlist);
return;
}
...
}
4
FART升级AOSP10
extern "C" void dumpArtMethod(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
...
const dex::CodeItem* code_item = artmethod->GetCodeItem();
const DexFile* dex_=artmethod->GetDexFile();
CodeItemDataAccessor accessor(*dex_, dex_->GetCodeItem(artmethod->GetCodeItemOffset()));
if (LIKELY(code_item != nullptr))
{
int code_item_len = 0;
uint8_t *item=(uint8_t *) code_item;
if (accessor.TriesSize()>0) {
const uint8_t *handler_data = accessor.GetCatchHandlerData();
uint8_t * tail = codeitem_end(&handler_data);
code_item_len = (int)(tail - item);
}else{
code_item_len = 16+accessor.InsnsSizeInCodeUnits()*2;
}
...
}
...
}
5
去FART指纹
6
FART的函数修复
dex起步探索(https://bbs.pediy.com/thread-268465.htm)
Youpk(https://github.com/Youlor/Youpk)
dexfixer(https://github.com/dqzg12300/dexfixer)
7
更深的主动调用
FART的主动调用深度
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (self== nullptr) {
dumpArtMethod(this);
return;
}
...
}
为什么需要更深的主动调用
.method public constructor <init>()V
.registers 2
goto :goto_c
:goto_1
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
return-void
:goto_c
const v0, 0x1669
invoke-static {v0}, Ls/h/e/l/l/H;->i(I)V
goto :goto_1
.end method
Youpk更深的主动调用
static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind;
void Unpacker::invokeAllMethods() {
...
auto methods = klass->GetDeclaredMethods(pointer_size);
Unpacker::enableFakeInvoke();
for (auto& m : methods) {
ArtMethod* method = &m;
if (!method->IsProxyMethod() && method->IsInvokable()) {
//获取参数个数
uint32_t args_size = (uint32_t)ArtMethod::NumArgRegisters(method->GetShorty());
if (!method->IsStatic()) {
args_size += 1;
}
//模拟参数
JValue result;
std::vector<uint32_t> args(args_size, 0);
if (!method->IsStatic()) {
mirror::Object* thiz = klass->AllocObject(self);
args[0] = StackReference<mirror::Object>::FromMirrorPtr(thiz).AsVRegValue();
}
method->Invoke(self, args.data(), args_size, &result, method->GetShorty());
}
}
Unpacker::disableFakeInvoke();
cJSON_ReplaceItemInObject(current, "status", cJSON_CreateString("Dumped"));
writeJson();
}
}
}
extern "C" void myfartInvoke(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
JValue *result=nullptr;
Thread *self=nullptr;
uint32_t temp=6;
uint32_t* args=&temp;
uint32_t args_size=6;
artmethod->Invoke(self, args, args_size, result, "fart");
}
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
...
//patch by Youlor
//++++++++++++++++++++++++++++
//如果是主动调用fake invoke并且不是native方法则强制走解释器
if (UNLIKELY(!runtime->IsStarted() || Dbg::IsForcedInterpreterNeededForCalling(self, this)
|| (Unpacker::isFakeInvoke(self, this) && !this->IsNative()))) {
//++++++++++++++++++++++++++++
if (IsStatic()) {
art::interpreter::EnterInterpreterFromInvoke(
self, this, nullptr, args, result, /*stay_in_interpreter*/ true);
} else {
mirror::Object* receiver =
reinterpret_cast<StackReference<mirror::Object>*>(&args[0])->AsMirrorPtr();
art::interpreter::EnterInterpreterFromInvoke(
self, this, receiver, args + 1, result, /*stay_in_interpreter*/ true);
}
} else {
//patch by Youlor
//++++++++++++++++++++++++++++
//如果是主动调用fake invoke并且是native方法则不执行
if (Unpacker::isFakeInvoke(self, this) && this->IsNative()) {
// Pop transition.
self->PopManagedStackFragment(fragment);
return;
}
//++++++++++++++++++++++++++++
...
}
...
}
void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receiver,
uint32_t* args, JValue* result,
bool stay_in_interpreter) {
...
JValue r = Execute(self, code_item, *shadow_frame, JValue(), stay_in_interpreter);
...
}
static inline JValue Execute(
Thread* self,
const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame,
JValue result_register,
bool stay_in_interpreter = false) SHARED_REQUIRES(Locks::mutator_lock_) {
...
} else if (kInterpreterImplKind == kSwitchImplKind) {
if (transaction_active) {
return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
false);
} else {
return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
false);
}
}
...
}
//patch by Youlor
//++++++++++++++++++++++++++++
#define PREAMBLE() \
do { \
inst_count++; \
bool dumped = Unpacker::beforeInstructionExecute(self, shadow_frame.GetMethod(), \
dex_pc, inst_count); \
if (dumped) { \
return JValue(); \
} \
if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \
shadow_frame.GetMethod(), dex_pc); \
} \
} while (false)
//++++++++++++++++++++++++++++
//继续解释执行返回false, dump完成返回true
bool Unpacker::beforeInstructionExecute(Thread *self, ArtMethod *method, uint32_t dex_pc, int inst_count) {
if (Unpacker::isFakeInvoke(self, method)) {
const uint16_t* const insns = method->GetCodeItem()->insns_;
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data = inst->Fetch16(0);
Instruction::Code opcode = inst->Opcode(inst_data);
//对于一般的方法抽取(非ijiami, najia), 直接在第一条指令处dump即可
if (inst_count == 0 && opcode != Instruction::GOTO && opcode != Instruction::GOTO_16 && opcode != Instruction::GOTO_32) {
Unpacker::dumpMethod(method);
return true;
}
//ijiami, najia的特征为: goto: goto_decrypt; nop; ... ; return; const vx, n; invoke-static xxx; goto: goto_origin;
else if (inst_count == 0 && opcode >= Instruction::GOTO && opcode <= Instruction::GOTO_32) {
return false;
} else if (inst_count == 1 && opcode >= Instruction::CONST_4 && opcode <= Instruction::CONST_WIDE_HIGH16) {
return false;
} else if (inst_count == 2 && (opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE)) {
//让这条指令真正的执行
Unpacker::disableFakeInvoke();
Unpacker::enableRealInvoke();
return false;
} else if (inst_count == 3) {
if (opcode >= Instruction::GOTO && opcode <= Instruction::GOTO_32) {
//写入时将第一条GOTO用nop填充
const Instruction* inst_first = Instruction::At(insns);
Instruction::Code first_opcode = inst_first->Opcode(inst->Fetch16(0));
CHECK(first_opcode >= Instruction::GOTO && first_opcode <= Instruction::GOTO_32);
ULOGD("found najia/ijiami %s", PrettyMethod(method).c_str());
switch (first_opcode)
{
case Instruction::GOTO:
Unpacker::dumpMethod(method, 2);
break;
case Instruction::GOTO_16:
Unpacker::dumpMethod(method, 4);
break;
case Instruction::GOTO_32:
Unpacker::dumpMethod(method, 8);
break;
default:
break;
}
} else {
Unpacker::dumpMethod(method);
}
return true;
}
Unpacker::dumpMethod(method);
return true;
}
return false;
}
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
bool interpret_one_instruction) {
...
//patch by Youlor
//++++++++++++++++++++++++++++
int inst_count = -1;
//++++++++++++++++++++++++++++
do {
dex_pc = inst->GetDexPc(insns);
shadow_frame.SetDexPC(dex_pc);
TraceExecution(shadow_frame, inst, dex_pc);
inst_data = inst->Fetch16(0);
switch (inst->Opcode(inst_data)) {
...
case Instruction::GOTO: {
PREAMBLE();
int8_t offset = inst->VRegA_10t(inst_data);
BRANCH_INSTRUMENTATION(offset);
if (IsBackwardBranch(offset)) {
HOTNESS_UPDATE();
self->AllowThreadSuspension();
}
inst = inst->RelativeAt(offset);
break;
}
...
case Instruction::INVOKE_STATIC: {
PREAMBLE();
bool success = DoInvoke<kStatic, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}
case Instruction::INVOKE_STATIC_RANGE: {
PREAMBLE();
bool success = DoInvoke<kStatic, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}
...
}
//patch by Youlor
//++++++++++++++++++++++++++++
bool dumped = Unpacker::afterInstructionExecute(self, shadow_frame.GetMethod(), dex_pc, inst_count);
if (dumped) {
return JValue();
}
//++++++++++++++++++++++++++++
} while (!interpret_one_instruction);
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
return result_register;
} // NOLINT(readability/fn_size)
bool Unpacker::afterInstructionExecute(Thread *self, ArtMethod *method, uint32_t dex_pc, int inst_count) {
const uint16_t* const insns = method->GetCodeItem()->insns_;
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data = inst->Fetch16(0);
Instruction::Code opcode = inst->Opcode(inst_data);
if (inst_count == 2 && (opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE)
&& Unpacker::isRealInvoke(self, method)) {
Unpacker::enableFakeInvoke();
Unpacker::disableRealInvoke();
}
return false;
}
FartExt更深的主动调用
#if ART_USE_CXX_INTERPRETER
static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind;
#else
static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
#endif
if envTrue(ctx, "ART_USE_CXX_INTERPRETER") {
cflags = append(cflags, "-DART_USE_CXX_INTERPRETER=1")
}
cflags: [
// ART is allowed to link to libicuuc directly
// since they are in the same module
"-DANDROID_LINK_SHARED_ICU4C",
"-Wno-error",
"-DART_USE_CXX_INTERPRETER=1"
],
extern "C" void fartextInvoke(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
if(artmethod->IsNative()||artmethod->IsAbstract()){
return;
}
JValue result;
//模拟参数
Thread *self=Thread::Current();
uint32_t temp[100]={0};
uint32_t* args=temp;
uint32_t args_size = (uint32_t)ArtMethod::NumArgRegisters(artmethod->GetShorty());
if (!artmethod->IsStatic()) {
args_size += 1;
}
//靠这个值,在后续来判断当前函数是否为主动调用。
result.SetI(111111);
LOG(ERROR) << "fartext fartextInvoke";
Unpacker_self_=self;
artmethod->Invoke(self, args, args_size, &result,artmethod->GetShorty());
}
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
...
//add
if (result!=nullptr && result->GetI()==111111){
LOG(ERROR) << "fartext artMethod::Invoke Method "<<this->PrettyMethod().c_str();
if (IsStatic()) {
art::interpreter::EnterInterpreterFromInvoke(
self, this, nullptr, args, result, /*stay_in_interpreter=*/ true);
}else{
//注意这里是把非静态的也当静态的方式处理的。避免使用引用类型参数。
art::interpreter::EnterInterpreterFromInvoke(
self, this, nullptr, args + 1, result, /*stay_in_interpreter=*/ true);
}
LOG(ERROR) << "fartext artMethod::Invoke Method Over "<<this->PrettyMethod().c_str();
self->PopManagedStackFragment(fragment);
return;
}
//add end
...
}
void EnterInterpreterFromInvoke(Thread* self,
ArtMethod* method,
ObjPtr<mirror::Object> receiver,
uint32_t* args,
JValue* result,
bool stay_in_interpreter) {
...
if (!method->IsStatic()) {
//add 避免使用引用类型的参数
if(result!=nullptr&&result->GetI()==111111){
shadow_frame->SetVReg(cur_reg, args[0]);
}else{
CHECK(receiver != nullptr);
shadow_frame->SetVRegReference(cur_reg, receiver);
}
//add end
//shadow_frame->SetVRegReference(cur_reg, receiver);
++cur_reg;
}
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
for (size_t shorty_pos = 0, arg_pos = 0; cur_reg < num_regs; ++shorty_pos, ++arg_pos, cur_reg++) {
DCHECK_LT(shorty_pos + 1, shorty_len);
switch (shorty[shorty_pos + 1]) {
case 'L': {
//add 避免使用引用类型的参数
if(result!=nullptr&&result->GetI()==111111){
shadow_frame->SetVReg(cur_reg, args[0]);
break;
}
//add end
ObjPtr<mirror::Object> o =
reinterpret_cast<StackReference<mirror::Object>*>(&args[arg_pos])->AsMirrorPtr();
shadow_frame->SetVRegReference(cur_reg, o);
break;
}
case 'J': case 'D': {
uint64_t wide_value = (static_cast<uint64_t>(args[arg_pos + 1]) << 32) | args[arg_pos];
shadow_frame->SetVRegLong(cur_reg, wide_value);
cur_reg++;
arg_pos++;
break;
}
default:
shadow_frame->SetVReg(cur_reg, args[arg_pos]);
break;
}
}
...
if (LIKELY(!method->IsNative())) {
//这里把我们主动调用函数的标志继续往后面传递
if(result!=nullptr&&result->GetI()==111111){
JValue r = Execute(self, accessor, *shadow_frame, *result, stay_in_interpreter);
if (result != nullptr) {
*result = r;
}
return;
}else{
JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter);
if (result != nullptr) {
*result = r;
}
}
}
...
}
template<bool do_access_check, bool transaction_active>
ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx) {
...
bool const interpret_one_instruction = ctx->interpret_one_instruction;
while (true) {
dex_pc = inst->GetDexPc(insns);
shadow_frame.SetDexPC(dex_pc);
TraceExecution(shadow_frame, inst, dex_pc);
inst_data = inst->Fetch16(0);
{
bool exit_loop = false;
InstructionHandler<do_access_check, transaction_active> handler(
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop);
//PREAMBLE变成这种方式调用了
if (!handler.Preamble()) {
if (UNLIKELY(exit_loop)) {
return;
}
if (UNLIKELY(interpret_one_instruction)) {
break;
}
continue;
}
}
switch (inst->Opcode(inst_data)) {
#define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, f, i, a, e, v) \
case OPCODE: { \
bool exit_loop = false; \
InstructionHandler<do_access_check, transaction_active> handler( \
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop); \
handler.OPCODE_NAME(); \
/* TODO: Advance 'inst' here, instead of explicitly in each handler */ \
if (UNLIKELY(exit_loop)) { \
return; \
} \
break; \
}
DEX_INSTRUCTION_LIST(OPCODE_CASE)
#undef OPCODE_CASE
}
if (UNLIKELY(interpret_one_instruction)) {
break;
}
}
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
ctx->result = ctx->result_register;
return;
} // NOLINT(readability/fn_size)
template<bool do_access_check, bool transaction_active>
ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx) {
...
//add
int32_t regvalue=ctx->result_register.GetI();
//这里很重要。需要把我们用来作为主动调用的值给改了。不然调用另外一个函数也会当成fart的主动调用的。
ctx->result_register=JValue();
int inst_count = -1; //当前第几个指令
bool flag=false; //第一个指令是否为goto
//add end
bool const interpret_one_instruction = ctx->interpret_one_instruction;
while (true) {
...
//add
inst_count++;
uint8_t opcode = inst->Opcode(inst_data)
//如果是主动调用
if(regvalue==111111){
//第一个指令是goto的处理
if(inst_count == 0 ){
if(opcode == Instruction::GOTO || opcode == Instruction::GOTO_16 || opcode == Instruction::GOTO_32){
LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=0 opcode==GOTO "<<shadow_frame.GetMethod()->PrettyMethod().c_str();
flag=true;
}else{
LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=0 opcode!=GOTO "<<shadow_frame.GetMethod()->PrettyMethod().c_str();
dumpArtMethod(shadow_frame.GetMethod());
break;
}
}
//第二个指令是const的处理
if(inst_count == 1){
if(opcode >= Instruction::CONST_4 && opcode <= Instruction::CONST_WIDE_HIGH16){
LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=1 opcode==CONST "<<shadow_frame.GetMethod()->PrettyMethod().c_str();
flag=true;
}else{
LOG(ERROR) << "fartext ExecuteSwitchImplCpp Switch inst_count=1 opcode!=CONST "<<shadow_frame.GetMethod()->PrettyMethod().c_str();
dumpArtMethod(shadow_frame.GetMethod());
break;
}
}
}
//add end
switch (opcode) {
#define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, f, i, a, e, v) \
case OPCODE: { \
bool exit_loop = false; \
InstructionHandler<do_access_check, transaction_active> handler( \
ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop); \
handler.OPCODE_NAME(); \
/* TODO: Advance 'inst' here, instead of explicitly in each handler */ \
if (UNLIKELY(exit_loop)) { \
return; \
} \
break; \
}
DEX_INSTRUCTION_LIST(OPCODE_CASE)
#undef OPCODE_CASE
}
//add
//指令执行结束后,再判断一下是不是主动调用的
if(regvalue==111111){
//如果这是第3个指令
if(inst_count==2&&flag){
//如果是下面两种操作码,就可以脱壳并结束了。
if(opcode == Instruction::INVOKE_STATIC || opcode == Instruction::INVOKE_STATIC_RANGE){
dumpArtMethod(shadow_frame.GetMethod());
ArtMethod::disableFartextInvoke();
break;
}
}
//如果主动调用的情况还能执行到第4个指令。那就直接脱壳并结束掉。
if(inst_count>2){
dumpArtMethod(shadow_frame.GetMethod());
ArtMethod::disableFartextInvoke();
break;
}
}
//add end
if (UNLIKELY(interpret_one_instruction)) {
break;
}
}
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
ctx->result = ctx->result_register;
return;
} // NOLINT(readability/fn_size)
8
流程图
9
更快的主动调用(暂未优化)
extern "C" void dumpArtMethod(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
//存放保存的dex路径
char *dexfilepath=(char*)malloc(sizeof(char)*1000);
if(dexfilepath==nullptr)
{
LOG(ERROR) << "ArtMethod::dumpArtMethodinvoked,methodname:"<<artmethod->PrettyMethod().c_str()<<"malloc 1000 byte failed";
return;
}
int result=0;
int fcmdline =-1;
char szCmdline[64]= {0};
char szProcName[256] = {0};
int procid = getpid();
//获取进程包名
sprintf(szCmdline,"/proc/%d/cmdline", procid);
fcmdline = open(szCmdline, O_RDONLY,0644);
if(fcmdline >0)
{
result=read(fcmdline, szProcName,256);
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,open cmdline file file error";
}
close(fcmdline);
}
if(szProcName[0])
{
const DexFile* dex_file = artmethod->GetDexFile();
const uint8_t* begin_=dex_file->Begin(); // Start of data.
size_t size_=dex_file->Size(); // Length of data.
memset(dexfilepath,0,1000);
int size_int_=(int)size_;
//创建目录
memset(dexfilepath,0,1000);
sprintf(dexfilepath,"%s","/sdcard/fart");
mkdir(dexfilepath,0777);
//创建目录
memset(dexfilepath,0,1000);
sprintf(dexfilepath,"/sdcard/fart/%s",szProcName);
mkdir(dexfilepath,0777);
//文件大小_dexfile.dex
memset(dexfilepath,0,1000);
sprintf(dexfilepath,"/sdcard/fart/%s/%d_dexfile.dex",szProcName,size_int_);
int dexfilefp=open(dexfilepath,O_RDONLY,0666);
//存在则略过
if(dexfilefp>0){
close(dexfilefp);
dexfilefp=0;
}else{
//dex的数据保存
int fp=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
if(fp>0)
{
result=write(fp,(void*)begin_,size_);
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,open dexfilepath file error";
}
fsync(fp);
close(fp);
memset(dexfilepath,0,1000);
//保存对应的classlist
sprintf(dexfilepath,"/sdcard/fart/%s/%d_classlist.txt",szProcName,size_int_);
int classlistfile=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
if(classlistfile>0)
{
for (size_t ii= 0; ii< dex_file->NumClassDefs(); ++ii)
{
const DexFile::ClassDef& class_def = dex_file->GetClassDef(ii);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
result=write(classlistfile,(void*)descriptor,strlen(descriptor));
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";
}
const char* temp="\n";
result=write(classlistfile,(void*)temp,1);
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write classlistfile file error";
}
}
fsync(classlistfile);
close(classlistfile);
}
}
}
//获取codeItem
const DexFile::CodeItem* code_item = artmethod->GetCodeItem();
if (LIKELY(code_item != nullptr))
{
int code_item_len = 0;
uint8_t *item=(uint8_t *) code_item;
//计算codeitem的大小
if (code_item->tries_size_>0) {
const uint8_t *handler_data = (const uint8_t *)(DexFile::GetTryItems(*code_item, code_item->tries_size_));
uint8_t * tail = codeitem_end(&handler_data);
code_item_len = (int)(tail - item);
}else{
code_item_len = 16+code_item->insns_size_in_code_units_*2;
}
//下面就是获取codeitem的idx和偏移,大小之类的。然后写入数据保存了
memset(dexfilepath,0,1000);
int size_int=(int)dex_file->Size();
uint32_t method_idx=artmethod->GetDexMethodIndexUnchecked();
sprintf(dexfilepath,"/sdcard/fart/%s/%d_ins_%d.bin",szProcName,size_int,(int)gettidv1());
int fp2=open(dexfilepath,O_CREAT|O_APPEND|O_RDWR,0666);
if(fp2>0){
//跳到文件末尾写入
lseek(fp2,0,SEEK_END);
memset(dexfilepath,0,1000);
int offset=(int)(item - begin_);
sprintf(dexfilepath,"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",artmethod->PrettyMethod().c_str(),method_idx,offset,code_item_len);
int contentlength=0;
while(dexfilepath[contentlength]!=0) contentlength++;
result=write(fp2,(void*)dexfilepath,contentlength);
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write ins file error";
}
long outlen=0;
char* base64result=base64_encode((char*)item,(long)code_item_len,&outlen);
result=write(fp2,base64result,outlen);
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write ins file error";
}
result=write(fp2,"};",2);
if(result<0)
{
LOG(ERROR) << "ArtMethod::dumpdexfilebyArtMethod,write ins file error";
}
fsync(fp2);
close(fp2);
if(base64result!=nullptr){
free(base64result);
base64result=nullptr;
}
}
}
}
if(dexfilepath!=nullptr)
{
free(dexfilepath);
dexfilepath=nullptr;
}
}
10
实战测试
Ps1:如果第二次打开应用发现没有触发主动调用,请清理应用:adb shell pm clear packageName
Ps2:如果不想等待60秒,想自己触发fart的主动调用,可以使用frida扩展
Ps3:如果想看logcat日志,搜索fartext即可,日志统一都添加了这个头部,方便查日志
11
联合frida扩展
function romClassesInvoke(classes){
Java.perform(function(){
klog("romClassesInvoke start load");
var fartExt=Java.use("cn.mik.Fartext");
if(!fartExt.fartWithClassList){
klog("fartExt中未找到fartWithClassList函数,可能是未使用Fartext的rom")
return ;
}
fartExt.fartWithClassList(classes);
})
}
function romFartAllClassLoader(){
Java.perform(function(){
var fartExt=Java.use("cn.mik.Fartext");
if(!fartExt.fartWithClassLoader){
klog("fartExt中未找到fartWithClassLoader函数,可能是未使用Fartext的rom");
return;
}
Java.enumerateClassLoadersSync().forEach(function(loader){
klog("romFartAllClassLoader to loader:"+loader);
if(loader.toString().indexOf("BootClassLoader")==-1){
klog("fart start loader:"+loader);
fartExt.fartWithClassLoader(loader);
}
})
});
}
12
思考
参考:https://bbs.pediy.com/thread-260052.htm以及看雪高研3W班课程看完后的整理与优化
看雪ID:misskings
https://bbs.pediy.com/user-home-659397.htm
# 往期推荐
1. V8利用初探 2019 StarCTF oob 复现分析
2. 新人PWN入坑总结
4. Cisco RV160W系列路由器漏洞:从1day分析到0day挖掘
5. 从SSL库的内存漫游开发dump自定义客户端证书的通杀脚本
球分享
球点赞
球在看
点击“阅读原文”,了解更多!