Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
Android Art Hook ¼¼Êõ·½°¸
 
×÷Õߣºi¼òÐÐÀ´×ÔÓÚ£ºCSDN ·¢²¼ÓÚ 2015-10-8
  5322  次浏览      27
 

0x1 ¿ªÊ¼

AnddroidÉϵÄART´Ó5.0Ö®ºó±ä³ÉĬÈϵÄÑ¡Ôñ£¬¿É¼ûARTµÄÖØÒªÐÔ£¬Ä¿Ç°¹ØÓÚDalvik Hook·½ÃæÑо¿µÄÎÄÕºܶ࣬µ«ÎÒÔÚÍøÉÏÈ´ÕÒ²»µ½¹ØÓÚART HookÏà¹ØµÄÎÄÕ£¬ÉõÖÁÁ¬¶¦¶¦´óÃûµÄXPosedºÍCydia Substrateµ½Ä¿Ç°ÎªÖ¹Ò²²»Ö§³ÖARTµÄHook¡£µ±È»ÎÒÏàÐÅ£¬¼¼Êõ·½°¸ËûÃǿ϶¨Êǵ쬹À¼Æ¿¨ÔÚ»úÐÍÊÊÅäÉϵÄÁË¡£

¼ÈÈ»ÍøÉÏÕÒ²»µ½Ïà¹ØµÄ×ÊÁÏ£¬ÓÚÊÇÎÒ¾ö¶¨×Ô¼º»¨Ð©Ê±¼äÈ¥Ñо¿Ò»Ï£¬ÖÕÓÚüSÌì²»¸ºÓÐÐÄÈË£¬ÎÒÕÒµ½ÁËÒ»¸öÇÐʵ¿ÉÐеķ½·¨£¬¼´±¾ÎÄËù½éÉܵķ½·¨¡£

Ó¦¸Ã˵Ã÷µÄÊDZ¾ÎÄËù½éÉܵķ½·¨¿Ï¶¨²»ÊÇ×îºÃµÄ£¬µ«´ó¼Ò¿´Íê±¾ÎÄÖ®ºó£¬Èç¹ûÄÜÆô·¢´ó¼ÒÕÒµ½¸üºÃµÄART Hook·½·¨£¬ÄÇÎÒÅ×שÒýÓñµÄÄ¿µÄ¾Í´ïµ½ÁË¡£·Ï»°²»¶à˵£¬ÎÒÃÇ¿ªÊ¼°É¡£

ÔËÐл·¾³: 4.4.2 ARTģʽµÄÄ£ÄâÆ÷

¿ª·¢»·¾³: Mac OS X 10.10.3

0x2 ARTÀà·½·¨¼ÓÔØ¼°Ö´ÐÐ

ÔÚARTÖÐÀà·½·¨µÄÖ´ÐÐÒª±ÈÔÚDalvikÖÐÒª¸´Ôӵö࣬DalvikÈç¹û³ýÈ¥JIT²¿·Ö£¬¿ÉÒÔÀí½âΪÊÇÒ»¸ö½âÎöÖ´ÐеÄÐéÄâ»ú£¬¶øARTÔòͬʱ°üº¬±¾µØÖ¸ÁîÖ´ÐкͽâÎöÖ´ÐÐÁ½ÖÖģʽ£¬Í¬Ê±ËùÉú³ÉµÄoatÎļþÒ²°üº¬Á½ÖÖÀàÐÍ£¬·Ö±ðÊÇportableºÍquick¡£portableºÍquickµÄÖ÷񻂿±ðÊǶÔÓÚ·½·¨µÄ¼ÓÔØ»úÖÆ²»Ïàͬ£¬quick´óÁ¿Ê¹ÓÃÁËLazy Load»úÖÆ£¬Òò´ËÓ¦ÓÃµÄÆô¶¯Ëٶȸü¿ì£¬µ«¼ÓÔØÁ÷³Ì¸ü¸´ÔÓ¡£ÆäÖÐquickÊÇ×÷ΪĬÈÏÑ¡ÏÒò´Ë±¾ÎÄËùÉæ¼°µÄ¼¼Êõ·ÖÎö¶¼ÊÇ»ùÓÚquickÀàÐ͵ġ£

ÓÉÓÚART´æÔÚ±¾µØÖ¸ÁîÖ´ÐкͽâÎöÖ´ÐÐÁ½ÖÖģʽ£¬Òò´ËÀà·½·¨Ö®¼ä²¢²»ÊÇÄÜÖ±½ÓÌø×ªµÄ£¬¶øÊÇͨ¹ýһЩԤÏȶ¨ÒåµÄbridgeº¯Êý½øÐÐ״̬ºÍÉÏÏÂÎĵÄÇл»£¬ÕâÀïÒýÓÃÒ»ÏÂÀÏÂÞ²©¿ÍÖеÄʾÒâͼ£º

µ±Ö´ÐÐij¸ö·½·¨Ê±£¬Èç¹ûµ±Ç°ÊDZ¾µØÖ¸ÁîÖ´ÐÐģʽ£¬Ôò»áÖ´ÐÐArtMethod::GetEntryPointFromCompiledCode()Ö¸ÏòµÄº¯Êý£¬·ñÔòÔòÖ´ÐÐArtMethod::GetEntryPointFromInterpreter()Ö¸ÏòµÄº¯Êý¡£Òò´Ëÿ¸ö·½·¨£¬¶¼ÓÐÁ½¸öÈë¿Úµã£¬·Ö±ð±£´æÔÚArtMethod::entry_point_from_compiled_code_ºÍArtMethod::entry_point_from_interpreter_¡£Á˽âÕâÒ»µã·Ç³£ÖØÒª£¬ºóÃæÎÒÃÇÖ÷Òª¾ÍÊÇÔÚÕâÁ½¸öÈë¿Ú×öÎÄÕ¡£

ÔÚ½²ÊöÔ­Àí֮ǰ£¬ÐèÒªÏȰÑÒÔÏÂÁ½¸öÁ÷³ÌÁ˽âÇå³þ£¬ÕâÀïµÄÄÚÈÝÒªÕ¹¿ªÊǷdz£ÅÓ´óµÄ£¬ÎÒÕë¶ÔHookµÄ¹Ø¼üµã£¬¼òÃ÷¶óÒªµÄÃèÊöһϣ¬µ«»¹ÊÇÇ¿ÁÒ½¨Òé´ó¼ÒÈ¥ÀÏÂ޵IJ©¿ÍÀïϸ¶ÁÒ»ÏÂÆäÖйØÓÚARTµÄ¼¸ÆªÎÄÕ¡£

ArtMethod¼ÓÔØÁ÷³Ì

Õâ¸ö¹ý³Ì·¢ÉúÔÚoat±»×°ÔؽøÄÚ´æ²¢½øÐÐÀà·½·¨Á´½ÓµÄʱºò£¬Àà·½·¨Á´½ÓµÄ´úÂëÔÚart/runtime/class_linker.ccÖеÄLinkCode£¬ÈçÏÂËùʾ£º

static void LinkCode(SirtRef<mirror::ArtMethod>& method, const OatFile::OatClass* oat_class, uint32_t method_index)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {

// Method shouldn't have already been linked.
DCHECK(method->GetEntryPointFromCompiledCode() == NULL);
// Every kind of method should at least get an invoke stub from the oat_method.
// non-abstract methods also get their code pointers.
const OatFile::OatMethod oat_method = oat_class->GetOatMethod(method_index);

// ÕâÀïĬÈÏ»á°Ñmethod::entry_point_from_compiled_code_ÉèÖÃoatmethodµÄcode
oat_method.LinkMethod(method.get());

// Install entry point from interpreter.
Runtime* runtime = Runtime::Current();
bool enter_interpreter = NeedsInterpreter(method.get(),

method->GetEntryPointFromCompiledCode()); //ÅжϷ½·¨ÊÇ·ñÐèÒª½âÎöÖ´ÐÐ

// ÉèÖýâÎöÖ´ÐеÄÈë¿Úµã
if (enter_interpreter) {
method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
} else {
method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
}

// ÏÂÃæÊÇÉèÖñ¾µØÖ¸ÁîÖ´ÐеÄÈë¿Úµã
if (method->IsAbstract()) {
method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());
return;
}

// ÕâÀï±È½ÏÄÑÀí½â£¬Èç¹ûÊǾ²Ì¬·½·¨£¬µ«²»ÊÇclinit£¬µ«ÐèÒª°Ñentry_point_from_compiled_code_ÉèÖÃΪGetResolutionTrampolineµÄ·µ»ØÖµ
if (method->IsStatic() && !method->IsConstructor()) {
// For static methods excluding the class initializer, install the trampoline.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
// after initializing class (see ClassLinker::InitializeClass method).
method->SetEntryPointFromCompiledCode(GetResolutionTrampoline(runtime->GetClassLinker()));
} else if (enter_interpreter) {
// Set entry point from compiled code if there's no code or in interpreter only mode.
method->SetEntryPointFromCompiledCode(GetCompiledCodeToInterpreterBridge());
}

if (method->IsNative()) {
// Unregistering restores the dlsym lookup stub.
method->UnregisterNative(Thread::Current());
}

// Allow instrumentation its chance to hijack code.
runtime->GetInstrumentation()->UpdateMethodsCode(method.get(),

method->GetEntryPointFromCompiledCode());
}

ͨ¹ýÉÏÃæµÄ´úÂëÎÒÃÇ¿ÉÒԵõ½£¬Ò»¸öArtMethodµÄÈë¿ÚÖ÷ÒªÓÐÒÔϼ¸ÖÖ£º

Interpreter2Interpreter¶ÔÓ¦artInterpreterToInterpreterBridge(art/runtime/interpreter/interpreter.cc);
Interpreter2CompledCode¶ÔÓ¦ artInterpreterToCompiledCodeBridge(/art/runtime/entrypoints/interpreter/interpreter_entrypoints.cc);
CompliedCode2Interpreter¶ÔÓ¦art_quick_to_interpreter_bridge(art/runtime/arch/arm/quick_entrypoints_arm.S);
CompliedCode2ResolutionTrampoline¶ÔÓ¦art_quick_resolution_trampoline(art/runtime/arch/arm/quick_entrypoints_arm.S);
CompliedCode2CompliedCodeÕâ¸öÈë¿ÚÊÇÖ±½ÓÖ¸ÏòoatÖеÄÖ¸ÁÏêϸ¿É¼ûOatMethod::LinkMethod;

ÆäÖе÷ÓÃÔ¼¶¨Ö÷ÒªÓÐÁ½ÖÖ£¬·Ö±ðÊÇ£º

typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame, JValue* result), ÕâÖÖ¶ÔÓ¦ÉÏÊö1£¬3Á½ÖÖÈë¿Ú£»
ʣϵÄ2£¬4£¬5ÈýÖÖÈë¿Ú¶ÔÓ¦µÄÊÇCompledCodeµÄÈë¿Ú£¬´úÂëÖв¢Ã»ÓÐÖ±½Ó¸ø³ö£¬µ«ÎÒÃÇͨ¹ý·ÖÎöArtMethod::InvokeµÄ·½·¨µ÷Ó㬾ͿÉÒÔÖªµÀÆäµ÷ÓÃÔ¼¶¨ÁË¡£Invoke¹ý³ÌÖлáµ÷ÓÃart_quick_invoke_stub(/art/runtime/arch/arm/quick_entrypoints_arm.S)£¬´úÂëÈçÏÂËùʾ£º

 /*
* Quick invocation stub.
* On entry:
* r0 = method pointer
* r1 = argument array or NULL for no argument methods
* r2 = size of argument array in bytes
* r3 = (managed) thread pointer
* [sp] = JValue* result
* [sp + 4] = result type char
*/
ENTRY art_quick_invoke_stub
push {r0, r4, r5, r9, r11, lr} @ spill regs
.save {r0, r4, r5, r9, r11, lr}
.pad #24
.cfi_adjust_cfa_offset 24
.cfi_rel_offset r0, 0
.cfi_rel_offset r4, 4
.cfi_rel_offset r5, 8
.cfi_rel_offset r9, 12
.cfi_rel_offset r11, 16
.cfi_rel_offset lr, 20
mov r11, sp @ save the stack pointer
.cfi_def_cfa_register r11
mov r9, r3 @ move managed thread pointer into r9
mov r4, #SUSPEND_CHECK_INTERVAL @ reset r4 to suspend check interval
add r5, r2, #16 @ create space for method pointer in frame
and r5, #0xFFFFFFF0 @ align frame size to 16 bytes
sub sp, r5 @ reserve stack space for argument array
add r0, sp, #4 @ pass stack pointer + method ptr as dest for memcpy
bl memcpy @ memcpy (dest, src, bytes)
ldr r0, [r11] @ restore method*
ldr r1, [sp, #4] @ copy arg value for r1
ldr r2, [sp, #8] @ copy arg value for r2
ldr r3, [sp, #12] @ copy arg value for r3
mov ip, #0 @ set ip to 0
str ip, [sp] @ store NULL for method* at bottom of frame
ldr ip, [r0, #METHOD_CODE_OFFSET] @ get pointer to the code
blx ip @ call the method
mov sp, r11 @ restore the stack pointer
ldr ip, [sp, #24] @ load the result pointer
strd r0, [ip] @ store r0/r1 into result pointer
pop {r0, r4, r5, r9, r11, lr} @ restore spill regs
.cfi_adjust_cfa_offset -24
bx lr
END art_quick_invoke_stub

¡°ldr ip, [r0, #METHOD_CODE_OFFSET]¡±Æäʵ¾ÍÊǰÑArtMethod::entry_point_from_compiled_code_¸³Öµ¸øip£¬È»ºóͨ¹ýblxÖ±½Óµ÷Óá£Í¨¹ýÕâ¶ÎССµÄ»ã±à´úÂ룬ÎÒÃǵóöÈç϶ÑÕ»µÄ²¼¾Ö£º

 -(low)
| caller(Method *) | <- sp
| arg1 | <- r1
| arg2 | <- r2
| arg3 | <- r3
| ... |
| argN |
| callee(Method *) | <- r0
+(high)

ÕâÖÖµ÷ÓÃÔ¼¶¨²¢²»ÊÇÆ½Ê±ÎÒÃÇËù¼ûµÄµ÷ÓÃÔ¼¶¨£¬Ö÷ÒªÌåÏÖÔÚ²ÎÊýµ±³¬¹ý4ʱ£¬²¢²»ÊÇ´Ósp¿ªÊ¼±£´æ£¬¶øÊÇ´Ósp + 20Õâ¸öλÖÿªÊ¼´æ´¢£¬ËùÒÔÕâ¾ÍÊÇΪʲôÔÚ´úÂëÀïentry_point_from_compiled_code_µÄÀàÐÍÊÇvoid *µÄÔ­ÒòÁË£¬ÒòΪÎÞ·¨ÓôúÂë±íʾ¡£

Àí½âºÃÕâ¸öµ÷ÓÃÔ¼¶¨¶ÔÎÒÃÇ·½°¸µÄʵÏÖÖÁ¹ØÖØÒª¡£

ArtMethodÖ´ÐÐÁ÷³Ì

ÉÏÃæÏêϸ½²ÊöÁËÀà·½·¨¼ÓÔØºÍÁ´½ÓµÄ¹ý³Ì£¬µ«ÔÚʵ¼ÊÖ´ÐеĹý³ÌÖУ¬Æäʵ»¹²»ÊÇÖ±½Óµ÷ÓÃArtMethodµÄentry_point(½âÎöÖ´Ðкͱ¾µØÖ¸ÁîÖ´ÐеÄÈë¿Ú)£¬ÎªÁ˼ӿìÖ´ÐÐËÙ¶È£¬ARTΪoatÎļþÖеÄÿ¸ödex´´½¨ÁËÒ»¸öDexCache£¨art/runtime/mirror/dex_cache.h£©½á¹¹£¬Õâ¸ö½á¹¹»á°´dexµÄ½á¹¹Éú³ÉһϵÁеÄÊý×飬ÕâÀïÎÒÃÇÖ»·ÖÎöËüÀïÃæµÄmethods×ֶΡ£ DexCache³õʼ»¯µÄ·½·¨ÊÇInit£¬ÊµÏÖÈçÏ£º

void DexCache::Init(const DexFile* dex_file,
String* location,
ObjectArray<String>* strings,
ObjectArray<Class>* resolved_types,
ObjectArray<ArtMethod>* resolved_methods,
ObjectArray<ArtField>* resolved_fields,
ObjectArray<StaticStorageBase>* initialized_static_storage) {
//...
//...
Runtime* runtime = Runtime::Current();
if (runtime->HasResolutionMethod()) {
// Initialize the resolve methods array to contain trampolines for resolution.
ArtMethod* trampoline = runtime->GetResolutionMethod();
size_t length = resolved_methods->GetLength();
for (size_t i = 0; i < length; i++) {
resolved_methods->SetWithoutChecks(i, trampoline);
}
}
}

¸ù¾Ýdex·½·¨µÄ¸öÊý£¬²úÉúÏàÓ¦³¤¶Èresolved_methodsÊý×飬Ȼºóÿһ¸ö¶¼ÓÃRuntime::GetResolutionMethod()·µ»ØµÄ½á¹û½øÐÐÌî³ä£¬Õâ¸ö·½·¨ÊÇÓÉRuntime::CreateResolutionMethod²úÉúµÄ£¬´úÂëÈçÏ£º

mirror::ArtMethod* Runtime::CreateResolutionMethod() {
mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod();
Thread* self = Thread::Current();
SirtRef<mirror::ArtMethod>
method(self, down_cast<mirror::ArtMethod*>(method_class->AllocObject(self)));
method->SetDeclaringClass(method_class);
// TODO: use a special method for resolution method saves
method->SetDexMethodIndex(DexFile::kDexNoIndex);
// When compiling, the code pointer will get set later when the image is loaded.
Runtime* r = Runtime::Current();
ClassLinker* cl = r->GetClassLinker();
method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl));
return method.get();
}

´Ómethod->SetDexMethodIndex(DexFile::kDexNoIndex)Õâ¾äµÃÖª£¬ËùÓеÄResolutionMethodµÄmethodIndexDexFile::kDexNoIndex¡£¶øResolutionMethodµÄentrypoint¾ÍÊÇÎÒÃÇÉÏÃæÈë¿Ú·ÖÎöÖеĵÚ4ÖÖÇé¿ö£¬GetResolutionTrampoline×îÖÕ·µ»ØµÄÈë¿ÚΪart_quick_resolution_trampoline(art/runtime/arch/arm/quick_entrypoints_arm.S)¡£ÎÒÃÇ¿´Ò»ÏÂÆäʵÏÖ´úÂ룺

  .extern artQuickResolutionTrampoline
ENTRY art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
mov r2, r9 @ pass Thread::Current
mov r3, sp @ pass SP
blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP)
cbz r0, 1f @ is code pointer null? goto exception
mov r12, r0
ldr r0, [sp, #0] @ load resolved method in r0
ldr r1, [sp, #8] @ restore non-callee save r1
ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
ldr lr, [sp, #44] @ restore lr
add sp, #48 @ rewind sp
.cfi_adjust_cfa_offset -48
bx r12 @ tail-call into actual code
1:
RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_resolution_trampoline

ÕûºÃ¼Ä´æÆ÷ºó£¬Ö±½ÓÌø×ªÖÁartQuickResolutionTrampoline(art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc)£¬½ÓÏÂÀ´ÎÒÃÇ·ÖÎöÕâ¸ö·½·¨µÄʵÏÖ£¨´ó¼Ò²»ÒªÔÎÁË¡£¡£¡££¬ÎÒ»á°ÑÎ޹ؽôÒªµÄ´úÂëÈ¥µô£©£º

// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
mirror::Object* receiver,
Thread* thread, mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsAndArgs);
// Start new JNI local reference state
JNIEnvExt* env = thread->GetJniEnv();
ScopedObjectAccessUnchecked soa(env);
ScopedJniEnvLocalRefState env_state(env);
const char* old_cause = thread->StartAssertNoThreadSuspension("Quick method resolution set up");

// Compute details about the called method (avoid GCs)
ClassLinker* linker = Runtime::Current()->GetClassLinker();
mirror::ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
InvokeType invoke_type;
const DexFile* dex_file;
uint32_t dex_method_idx;
if (called->IsRuntimeMethod()) {
//...
//...
} else {
invoke_type = kStatic;
dex_file = &MethodHelper(called).GetDexFile();
dex_method_idx = called->GetDexMethodIndex();
}

//...

// Resolve method filling in dex cache.
if (called->IsRuntimeMethod()) {
called = linker->ResolveMethod(dex_method_idx, caller, invoke_type);
}

const void* code = NULL;
if (LIKELY(!thread->IsExceptionPending())) {
//...

linker->EnsureInitialized(called_class, true, true);

//...
}
// ...
return code;
}


inline bool ArtMethod::IsRuntimeMethod() const {
return GetDexMethodIndex() == DexFile::kDexNoIndex;
}

called->IsRuntimeMethod()ÓÃÓÚÅжϵ±Ç°·½·¨ÊÇ·ñΪResolutionMethod¡£Èç¹ûÊÇ£¬ÄÇô¾Í×ßClassLinker::ResolveMethodÁ÷³ÌÈ¥»ñÈ¡ÕæÕýµÄ·½·¨£¬¼û´úÂ룺

mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
uint32_t method_idx,
mirror::DexCache* dex_cache,
mirror::ClassLoader* class_loader,
const mirror::ArtMethod* referrer,
InvokeType type) {
DCHECK(dex_cache != NULL);
// Check for hit in the dex cache.
mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
if (resolved != NULL) {
return resolved;
}
// Fail, get the declaring class.
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);

if (klass == NULL) {
DCHECK(Thread::Current()->IsExceptionPending());
return NULL;
}

// Scan using method_idx, this saves string compares but will only hit for matching dex
// caches/files.
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(dex_cache, method_idx);
break;
case kInterface:
resolved = klass->FindInterfaceMethod(dex_cache, method_idx);
DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface());
break;
case kSuper: // Fall-through.
case kVirtual:
resolved = klass->FindVirtualMethod(dex_cache, method_idx);
break;
default:
LOG(FATAL) << "Unreachable - invocation type: " << type;
}

if (resolved == NULL) {
// Search by name, which works across dex files.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL));
switch (type) {
case kDirect: // Fall-through.
case kStatic:
resolved = klass->FindDirectMethod(name, signature);
break;
case kInterface:
resolved = klass->FindInterfaceMethod(name, signature);
DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface());
break;
case kSuper: // Fall-through.
case kVirtual:
resolved = klass->FindVirtualMethod(name, signature);
break;
}
}


if (resolved != NULL) {
// Be a good citizen and update the dex cache to speed subsequent calls.
dex_cache->SetResolvedMethod(method_idx, resolved);
return resolved;
} else {
// ...
}
}

ÆäʵÕâÀï·¢ÉúÁË¡°Á¬Ëø·´Ó¦¡±£¬ClassLinker::ResolveType×ßµÄÁ÷³Ì£¬¸úResolveMethodÊǷdz£ÀàËÆµÄ£¬ÓÐÐËȤµÄÅóÓÑ¿ÉÒÔ¸úһϡ£
ÕÒµ½½âÎöºóµÄklass£¬ÔÙ¾­¹ýÒ»ÂÖ·è¿ñµÄËÑË÷£¬°ÑÕÒµ½µÄresolvedͨ¹ýDexCache::SetResolvedMethod¸²¸Çµô֮ǰµÄ¡°ÌæÉí¡±¡£µ±ÔÙÏ´ÎÔÙͨ¹ýResolveMethod½âÎö·½·¨Ê±£¬¾Í¿ÉÒÔÖ±½Ó°Ñ¸Ã·½·¨·µ»Ø£¬²»ÐèÒªÔÙ½âÎöÁË¡£

ÎÒÃǻعýÍ·À´ÔÙÖØÐ¡°¸´ÏÖ¡±Ò»ÏÂÕâ¸ö¹ý³Ì£¬µ±ÎÒÃÇÊ״ε÷ÓÃij¸öÀà·½·¨£¬Æä¹ý³ÌÈçÏÂËùʾ£º

µ÷ÓÃResolutionMethodµÄentrypoint£¬½øÈëart_quick_resolution_trampoline£»
art_quick_resolution_trampolineÌø×ªµ½artQuickResolutionTrampoline£»
artQuickResolutionTrampolineµ÷ÓÃClassLinker::ResolveMethod½âÎöÀà·½·¨£»
ClassLinker::ResolveMethodµ÷ÓÃClassLinkder::ResolveType½âÎöÀ࣬ÔÙ´Ó½âÎöºÃµÄÀàѰÕÒÕæÕýµÄ·½·¨£»
µ÷ÓÃDexCache::SetResolvedMethod£¬ÓÃÕæÕýµÄ·½·¨¸²¸Çµô¡°ÌæÉí¡±·½·¨£»
µ÷ÓÃÕæÕý·½·¨µÄentrypoint´úÂ룻

Ò²ÐíÄã»áÎÊ£¬ÎªÊ²Ã´Òª°Ñ¹ý³Ì¸ãµÃÕâÃ´ÈÆ£¿ Ò»Çж¼ÊÇΪÁËÑÓ³Ù¼ÓÔØ£¬Ìá¸ßÆô¶¯ËÙ¶È£¬Õâ¸ö¹ý³Ì¸úELF LinkerµÄPLT/GOT·ûºÅÖØ¶¨ÏòµÄ¹ý³ÌÊÇºÎÆäÏàËÆ°¡£¬ËùÒÔ¼¼Êõ¶¼ÊÇÏëͨµÄ£¬Ò»Í¨°ÙÃ÷¡£

0x3 Hook ArtMethod

ͨ¹ýÉÏÊöArtMethod¼ÓÔØºÍÖ´ÐÐÁ½¸öÁ÷³ÌµÄ·ÖÎö£¬¶ÔÓÚÈçºÎHook ArtMethod£¬ÎÒÏëµ½ÁËÁ½¸ö·½°¸£¬·Ö±ð

ÐÞ¸ÄDexCachÀïµÄmethods£¬°ÑÀïÃæµÄentrypointÐÞ¸ÄΪ×Ô¼ºµÄ£¬×öÒ»¸öÖÐת´¦Àí£»

Ö±½ÓÐ޸ļÓÔØºóµÄArtMethodµÄentrypoint£¬Í¬Ñù×öÒ»¸öÖÐת´¦Àí£»

ÉÏÃæÁ½¸ö·½·¨¶¼ÊÇ¿ÉÐе쬵«ÓÉÓÚÎÒÏ£ÍûÕû¸öÏîÄ¿¿ÉÒÔÔÚNDK»·¾³(¶ø²»ÊÇÔÚÔ´ÂëÏÂ)ϱàÒ룬ÒòΪ¾Í²ÉÓÃÁË·½°¸2£¬ÒòΪͨ¹ýJNIµÄ½Ó¿Ú¾Í¿ÉÒÔÖ±½Ó»ñÈ¡½âÎöÖ®ºóµÄArtMethod£¬¿ÉÒÔ¼õÉٺܶàÎļþÒÀÀµ¡£

»Øµ½Ç°ÃæµÄµ÷ÓÃÔ¼¶¨£¬Ã¿¸öArtMethod¶¼ÓÐÁ½¸öÔ¼¶¨£¬°´µÀÀíÎÒÃÇÓ¦¸Ã×¼±¸Á½¸öÖÐתº¯ÊýµÄ£¬µ«ÕâÀïÎÒÃDz»¿¼ÂÇÇ¿ÖÆ½âÎöģʽִÐУ¬ËùÒÔÖ»Òª´¦ÀíºÃentry_point_from_compiled_codeµÄÖÐת¼´¿É¡£

Ê×ÏÈ£¬ÎÒÃÇÕÒµ½¶ÔÓ¦µÄ·½·¨£¬Ïȱ£´æÆäentrypoint£¬È»ºóÔÙ°ÑÎÒÃǵÄÖÐתº¯Êýart_quick_dispatcher¸²¸Ç£¬´úÂëÈçÏÂËùʾ£º

extern int __attribute__ ((visibility ("hidden"))) art_java_method_hook(JNIEnv* env, HookInfo *info) {
const char* classDesc = info->classDesc;
const char* methodName = info->methodName;
const char* methodSig = info->methodSig;
const bool isStaticMethod = info->isStaticMethod;

// TODO we can find class by special classloader what do just like dvm
jclass claxx = env->FindClass(classDesc);
if(claxx == NULL){
LOGE("[-] %s class not found", classDesc);
return -1;
}

jmethodID methid = isStaticMethod ?
env->GetStaticMethodID(claxx, methodName, methodSig) :
env->GetMethodID(claxx, methodName, methodSig);

if(methid == NULL){
LOGE("[-] %s->%s method not found", classDesc, methodName);
return -1;
}

ArtMethod *artmeth = reinterpret_cast<ArtMethod *>(methid);

if(art_quick_dispatcher != artmeth->GetEntryPointFromCompiledCode()){
uint64_t (*entrypoint)(ArtMethod* method, Object *thiz, u4 *arg1, u4 *arg2);
entrypoint = (uint64_t (*)(ArtMethod*, Object *, u4 *, u4 *))artmeth->GetEntryPointFromCompiledCode();

info->entrypoint = (const void *)entrypoint;
info->nativecode = artmeth->GetNativeMethod();

artmeth->SetEntryPointFromCompiledCode((const void *)art_quick_dispatcher);

// save info to nativecode :)
artmeth->SetNativeMethod((const void *)info);

LOGI("[+] %s->%s was hooked\n", classDesc, methodName);
}else{
LOGW("[*] %s->%s method had been hooked", classDesc, methodName);
}

return 0;
}

ÎÒÃǹؼüµÄÐÅϢͨ¹ýArtMethod::SetNativeMethod±£´æÆðÀ´ÁË¡£

¿¼Âǵ½ARTÌØÊâµÄµ÷ÓÃÔ¼¶¨£¬art_quick_dispatcherÖ»ÄÜÓûã±àʵÏÖÁË£¬°Ñ¼Ä´æÆ÷Êʵ±µÄµ÷Õûһϣ¬ÔÙÌø×ªµ½ÁíÒ»¸öº¯ÊýartQuickToDispatcher£¬ÕâÑù¾Í¿ÉÒԺܷ½±ãÓÃc/c++·ÃÎʲÎÊýÁË¡£

ÏÈ¿´Ò»ÏÂart_quick_dispatcherº¯ÊýµÄʵÏÖÈçÏ£º

/*
* Art Quick Dispatcher.
* On entry:
* r0 = method pointer
* r1 = arg1
* r2 = arg2
* r3 = arg3
* [sp] = method pointer
* [sp + 4] = addr of thiz
* [sp + 8] = addr of arg1
* [sp + 12] = addr of arg2
* [sp + 16] = addr of arg3
* and so on
*/
.extern artQuickToDispatcher
ENTRY art_quick_dispatcher
push {r4, r5, lr} @ sp - 12
mov r0, r0 @ pass r0 to method
str r1, [sp, #(12 + 4)]
str r2, [sp, #(12 + 8)]
str r3, [sp, #(12 + 12)]
mov r1, r9 @ pass r1 to thread
add r2, sp, #(12 + 4) @ pass r2 to args array
add r3, sp, #12 @ pass r3 to old SP
blx artQuickToDispatcher @ (Method* method, Thread*, u4 **, u4 **)
pop {r4, r5, pc} @ return on success, r0 and r1 hold the result
END art_quick_dispatcher

ÎÒ°Ñr2Ö¸Ïò²ÎÊýÊý×飬ÕâÑù¾ÍÎÒÃǾͿÉÒԷdz£·½±ãµÄ·ÃÎÊËùÓвÎÊýÁË¡£ÁíÍ⣬ÎÒÓÃr3±£´æÁ˾ɵÄspµØÖ·£¬ÕâÑùÊÇΪºóÃæµ÷ÓÃÔ­À´µÄentrypoint×ö×¼±¸µÄ¡£ÎÒÃÇÏÈ¿´¿´artQuickToDispatcherµÄʵÏÖ£º

extern "C" uint64_t artQuickToDispatcher(ArtMethod* method, Thread *self, u4 **args, u4 **old_sp){
HookInfo *info = (HookInfo *)method->GetNativeMethod();
LOGI("[+] entry ArtHandler %s->%s", info->classDesc, info->methodName);

// If it not is static method, then args[0] was pointing to this
if(!info->isStaticMethod){
Object *thiz = reinterpret_cast<Object *>(args[0]);
if(thiz != NULL){
char *bytes = get_chars_from_utf16(thiz->GetClass()->GetName());
LOGI("[+] thiz class is %s", bytes);
delete bytes;
}
}

const void *entrypoint = info->entrypoint;
method->SetNativeMethod(info->nativecode); //restore nativecode for JNI method
uint64_t res = art_quick_call_entrypoint(method, self, args, old_sp, entrypoint);

JValue* result = (JValue* )&res;
if(result != NULL){
Object *obj = result->l;
char *raw_class_name = get_chars_from_utf16(obj->GetClass()->GetName());

if(strcmp(raw_class_name, "java.lang.String") == 0){
char *raw_string_value = get_chars_from_utf16((String *)obj);
LOGI("result-class %s, result-value \"%s\"", raw_class_name, raw_string_value);
free(raw_string_value);
}else{
LOGI("result-class %s", raw_class_name);
}

free(raw_class_name);
}

// entrypoid may be replaced by trampoline, only once.
// if(method->IsStatic() && !method->IsConstructor()){

entrypoint = method->GetEntryPointFromCompiledCode();
if(entrypoint != (const void *)art_quick_dispatcher){
LOGW("[*] entrypoint was replaced. %s->%s", info->classDesc, info->methodName);

method->SetEntryPointFromCompiledCode((const void *)art_quick_dispatcher);
info->entrypoint = entrypoint;
info->nativecode = method->GetNativeMethod();
}

method->SetNativeMethod((const void *)info);

// }

return res;
}

ÕâÀï²ÎÊý½âÎö¾Í²»Ïêϸ˵ÁË£¬½ÓÏÂÀ´ÊÇ×ÊÖµÄÎÊÌ⡪¡ªÈçºÎÖØÐµ÷»ØÔ­À´µÄentrypoint¡£

ÕâÀïµÄ¹Ø¼üµãÊÇÒª»¹Ô­Ö®Ç°µÄ¶ÑÕ»²¼¾Ö£¬art_quick_call_entrypoint¾ÍÊǸºÔðÍê³ÉÕâ¸ö¹¤×÷µÄ£¬ÆäʵÏÖÈçÏÂËùʾ£º

/*
*
* Art Quick Call Entrypoint
* On entry:
* r0 = method pointer
* r1 = thread pointer
* r2 = args arrays pointer
* r3 = old_sp
* [sp] = entrypoint
*/
ENTRY art_quick_call_entrypoint
push {r4, r5, lr} @ sp - 12
sub sp, #(40 + 20) @ sp - 40 - 20
str r0, [sp, #(40 + 0)] @ var_40_0 = method_pointer
str r1, [sp, #(40 + 4)] @ var_40_4 = thread_pointer
str r2, [sp, #(40 + 8)] @ var_40_8 = args_array
str r3, [sp, #(40 + 12)] @ var_40_12 = old_sp
mov r0, sp
mov r1, r3
ldr r2, =40
blx memcpy @ memcpy(dest, src, size_of_byte)
ldr r0, [sp, #(40 + 0)] @ restore method to r0
ldr r1, [sp, #(40 + 4)]
mov r9, r1 @ restore thread to r9
ldr r5, [sp, #(40 + 8)] @ pass r5 to args_array
ldr r1, [r5] @ restore arg1
ldr r2, [r5, #4] @ restore arg2
ldr r3, [r5, #8] @ restore arg3
ldr r5, [sp, #(40 + 20 + 12)] @ pass ip to entrypoint
blx r5
add sp, #(40 + 20)
pop {r4, r5, pc} @ return on success, r0 and r1 hold the result
END art_quick_call_entrypoint

ÕâÀïÎÒ͵ÀÁÁË£¬Ö±½ÓÉêÇëÁË10¸ö²ÎÊýµÄ¿Õ¼ä£¬ÔÙʹÓÃ֮ǰ´«½øÈëÀ´µÄold_sp½øÐлָ´£¬Ê¹ÓÃmemcpyÖ±½Ó¸´ÖÆ40×Ö½Ú¡£Ö®ºó¾ÍÊÇ»¹Ô­r0, r1, r2, r3, r9µÄÖµÁË¡£µ÷ÓÃentrypointÍêºó£¬½á¹û±£´æÔÚr0ºÍr1£¬ÔÙ·µ»Ø¸øartQuickToDispatcher¡£

ÖÁ´Ë£¬Õû¸öART Hook¾Í·ÖÎöÍê±ÏÁË¡£

0x4 4.4Óë5.XÉÏʵÏÖµÄÇø±ð

ÎÒµÄÕû¸ö·½°¸¶¼ÊÇÔÚ4.4ÉϲâÊԵģ¬Ö÷ÒªÊÇÒòΪÎÒÖ»ÓÐ4.4µÄÔ´Â룬¶øÇÒÓ²Å̿ռ䲻×㣬ʵÔÚ×°²»ÏÂ5.xµÄÔ´ÂëÁË¡£µ«Õû¸ö˼·£¬ÊÇÍêÈ«¿ÉÒÔÌ×ÓÃÓÃ5.XÉÏ¡£ÁíÍ⣬5.XµÄʵÏÖ´úÂë±È4.4Éϸ´ÔÓÁ˺ܶ࣬·ñÄÜÏñÎÒÕâÑùÔÚNDKϱàÒëÍê³É¾Í²»ÖªµÀÁË¡£

Õý³£µÄ4.4Ä£ÄâÆ÷ÊÇÒÔdalvikÆô¶¯µÄ£¬Òªµ½ÉèÖÃÀï¸ÄΪart£¬ÕâÀï»áÒªÇó½øÐÐÖØÆô£¬µ«Ò»°ãÎÞЧ£¬ÎÒÃÇÊÖ¶¯¹Ø±ÕÔÙÖØÐ´ò¿ª¾ÍOKÁË£¬µ«ÐèÒªµÈÉÏÒ»¶Îʱ¼ä²Å¿ÉÒÔ¡£

0x5 ½áÊø

ËäÈ»ÕâÆªÎÄÕÂÖ»ÊǽéÉÜÁËArt HookµÄ¼¼Êõ·½°¸£¬µ«ÆäÖеļ¼ÊõÔ­Àí£¬¶ÔÓÚÈçºÎÔÚARTÉϽøÐдúÂë¼Ó¹Ì¡¢¶¯Ì¬´úÂ뻹ԭµÈµÈÒ²ÊǺÜÓÐÆô·¢ÐÔ¡£

ÀÏÑù×Ó£¬Õû¸öÏîÄ¿µÄ´úÂ룬ÎÒÒѾ­Ìá½»µ½https://github.com/boyliang/AllHookInOne£¬´ó¼ÒÓöµ½Ê²Ã´ÎÊÌ⣬»¶Ó­ÌáÎÊ£¬ÓÐÎÊÌâ¼ÇµÃ·´À¡¡£

¶ÔÁË£¬ÇëÓÃhttps://github.com/boyliang/ndk-patch¸øÄãµÄNDK´òÒ»ÏÂpatch¡£

   
5322 ´Îä¯ÀÀ       27
 
Ïà¹ØÎÄÕÂ

ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖÓë̽ÌÖ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
 
Ïà¹ØÎĵµ

Android_UI¹Ù·½Éè¼Æ½Ì³Ì
ÊÖ»ú¿ª·¢Æ½Ì¨½éÉÜ
androidÅÄÕÕ¼°ÉÏ´«¹¦ÄÜ
Android½²ÒåÖÇÄÜÊÖ»ú¿ª·¢
Ïà¹Ø¿Î³Ì

Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
Androidϵͳ¿ª·¢
AndroidÓ¦Óÿª·¢
ÊÖ»úÈí¼þ²âÊÔ
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

androidÈË»ú½çÃæÖ¸ÄÏ
AndroidÊÖ»ú¿ª·¢£¨Ò»£©
AndroidÊÖ»ú¿ª·¢£¨¶þ£©
AndroidÊÖ»ú¿ª·¢£¨Èý£©
AndroidÊÖ»ú¿ª·¢£¨ËÄ£©
iPhoneÏûÏ¢ÍÆËÍ»úÖÆÊµÏÖ̽ÌÖ
ÊÖ»úÈí¼þ²âÊÔÓÃÀýÉè¼ÆÊµ¼ù
ÊÖ»ú¿Í»§¶ËUI²âÊÔ·ÖÎö
ÊÖ»úÈí¼þ×Ô¶¯»¯²âÊÔÑо¿±¨¸æ

Android¸ß¼¶Òƶ¯Ó¦ÓóÌÐò
AndroidÓ¦Óÿª·¢
Androidϵͳ¿ª·¢
ÊÖ»úÈí¼þ²âÊÔ
ǶÈëʽÈí¼þ²âÊÔ
AndroidÈí¡¢Ó²¡¢ÔÆÕûºÏ

ÁìÏÈIT¹«Ë¾ android¿ª·¢Æ½Ì¨×î¼Ñʵ¼ù
±±¾© Android¿ª·¢¼¼Êõ½ø½×
ijÐÂÄÜÔ´ÁìÓòÆóÒµ Android¿ª·¢¼¼Êõ
ijº½Ì칫˾ Android¡¢IOSÓ¦ÓÃÈí¼þ¿ª·¢
°¢¶û¿¨ÌØ LinuxÄÚºËÇý¶¯
°¬Ä¬Éú ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
Î÷ÃÅ×Ó Ç¶Èëʽ¼Ü¹¹Éè¼Æ