寒冰大佬的FART带动了不少新的主动调用思想的抽取壳方案,看了上面这篇文章感觉意犹未尽,当然是要动手实践一翻来优化一波。于是我重新翻阅FART和Youpk的源码,我准备和那位大佬一样,参考Youpk在FART的基础上进行升级改造。
开工前先明确出我的需求:
对指定进程脱壳,非目标进程不要执行脱壳线程
对指定类列表进行脱壳
将FART升级到aosp10实现
去FART指纹
FART保存出的函数修复合并为dex
实现FART更深的主动调用
更快的主动调用(暂未优化)
1
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() {@Overridepublic void run() {// TODO Auto-generated method stubtry {Log.e("ActivityThread", "start sleep......");Thread.sleep(1 * 60 * 1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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
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
6
7
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 2goto :goto_c:goto_1nopnopnopnopnopnopnopnopnopnopreturn-void:goto_cconst v0, 0x1669invoke-static {v0}, Ls/h/e/l/l/H;->i(I)Vgoto :goto_1.end method
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完成返回truebool 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;}
#if ART_USE_CXX_INTERPRETERstatic constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind;#elsestatic 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) {...//addif (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 endObjPtr<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) {...//addint32_t regvalue=ctx->result_register.GetI();//这里很重要。需要把我们用来作为主动调用的值给改了。不然调用另外一个函数也会当成fart的主动调用的。ctx->result_register=JValue();int inst_count = -1; //当前第几个指令bool flag=false; //第一个指令是否为goto//add endbool const interpret_one_instruction = ctx->interpret_one_instruction;while (true) {...//addinst_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 endswitch (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 endif (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.dexmemset(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);//保存对应的classlistsprintf(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);}}}//获取codeItemconst 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
11
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
思考
《安卓高级研修班(网课)》
春季班开始招生!
有一定基础的初、中级安卓逆向研究员,迫切希望提高自身能力、学习能力强,升职加薪意愿强烈、学习意愿强烈。
专属班主任,敦促学习、鼓励士气;良好的抱团学习的氛围;
可以参加《安卓高级研修班》线下班,鼓励线下交流,与大佬谈笑风生;
注意2W班和3W班是完全独立噢,没有交集;
开班时间:2022年春季开班
PS:以上为总体服务计划,具体课程时间(段)安排以最终合同约定的课程表为准。
高研网课 | 就业班 | 强化班 |
月薪三万计划 | 16999元 | 8599元 |
月薪两万计划 | 11199元 | 5599元 |
就业班附带包就业服务(须达到合同规定的毕业标准),签合同保证就业及薪资,达不到退全款;
就业班有入学考核,缴费成功后进入考核流程,考核不通过退全款;
考核流程会包括简历筛选、班主任和老师(电话)面试等环节;
强化班仅去除包就业服务,并且无入学考核,其余与就业班完全相同;
就业班与强化班一起授课,合计35人一个班,教学上不做任何区分。
《安卓高级研修班》全系列无任何金融计划,纯预付;无任何金融套路。
网络课程为虚拟商品,购买之前可以观看下述试看内容,购买成功之后不接受退款。
扫码立即报名!
扫码免费试看
扫码免费试看
免责条款
以上所有宣传稿件内容均不作为服务承诺,最终以实际签订培训合同为准。
课程大纲与细目会根据教学反馈不断优化、调整与更新,实际授课可能与宣传主题略有不同;
Q:有优惠么?!有优惠么?!有优惠么?!重要的事情说三遍!!
一部pixel手机(sailfish)(安卓8脱壳镜像)
安卓源码开发编译调试环境SSD移动硬盘500G
A:月薪三万计划的内容与线下班的内容是一样的,我们在线下班沉淀大家的切实的需求和疑问,重新编排和制作内容作为网课与大家分享。月薪两万计划的内容由三万计划的讲师全新制作,充分体现工作场景一线的需求,更加贴近实战、实用,有用、好用。
A:目前针对ollvm和vmp,任何所谓的自动化,都是带很多前提和条件限制的;目前最快的还原ollvm或vmp的方法,还是手动分析,一般快则两三日、慢则一两周,基本上可以还原出来。
A:月薪两万计划推荐至少有实际安卓安全岗位工作经验一年以上为宜。初学者可以先看我们安卓版主非虫大佬的《Android软件安全权威指南》等安卓安全书籍进行入门,在看雪论坛看帖发帖提升自身水平,本套课程建议有工作经验的老手前来充电学习。
月薪三万计划视大家实际需求而定,一般看得懂目录及想要学习的人自己就懂,大家不用盲目跟风。如果看不懂目录及不理解目录的具体含义及意义,建议先从两万计划学起,多积累技术和经验。
A:不需要,互相独立的。月薪两万计划的定位更加偏向工作岗位一线逆向需求,月薪三万计划则更加偏向于高级调试技巧,二者互为补充,相辅相成。有非常多地大佬两个计划一起报名了,我们也会确保直播时间不会冲突。
A:每一场直播都有回放,在看雪课程中可以观看。
A:就业班是需要考核的。考核流程是先缴费报名,然后开始。会经过简历、(远程)一面和二面。通过之后补差价,不通过退全款。
# 十一月
# 十月
《dexvmp后的算法逆向分析和还原》
《使用unicorn对ollvm字符串进行解密》
《Frida追踪定Socket接口自吐游戏APK的服务器IP和地址》
Frida hook Java/Native与init_array 自吐最终方案 》
# 九月
《macOS安装调试llvm入门》
《fart的理解和分析过程》
《使用ollvm自定义简单的字符串加密》
《使用ida trace来还原ollvm混淆的非标准算法》
# 八月
# 七月
# 六月
从三道题目入手入门frida
单纯使用Frida书写类抽取脱壳工具的一些心路历程和实践
某聊天app的音视频通话逆向
# 五月
初试IDA&FRIDA联合调试简单ollvm保护的加密函数源码
# 四月
某抽取壳的原理简析
frida辅助脱壳
# 三月
报 名 地 址
扫码立即报名!
球分享
球点赞
球在看
点击“阅读原文”,了解更多!