其實在寒冰大佬的介紹種已經很詳細了考婴,但是還是要自己過一下才行
https://bbs.pediy.com/thread-252630.htm
ActivityThread
首先對比一下這里,看一下做了什么操作
-
handleBindApplication
綁定application的時候打了一句log
-
performLaunchActivity
啟動activity的時候打了log尼夺,并且開線程開始dump
public static void fartthread() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Log.e("ActivityThread", "start sleep,wait for fartthread start......");
Thread.sleep(1 * 60 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("ActivityThread", "sleep over and start fartthread");
fart();
Log.e("ActivityThread", "fart run over");
}
}).start();
}
睡60s,然后調用fart方法
public static void fart() {
ClassLoader appClassloader = getClassloader();
List<Object> dexFilesArray = new ArrayList<Object>();
Field pathList_Field = (Field) getClassField(appClassloader, "dalvik.system.BaseDexClassLoader", "pathList");
Object pathList_object = getFieldOjbect("dalvik.system.BaseDexClassLoader", appClassloader, "pathList");
Object[] ElementsArray = (Object[]) getFieldOjbect("dalvik.system.DexPathList", pathList_object, "dexElements");
Field dexFile_fileField = null;
try {
dexFile_fileField = (Field) getClassField(appClassloader, "dalvik.system.DexPathList$Element", "dexFile");
} catch (Exception e) {
e.printStackTrace();
}
Class DexFileClazz = null;
try {
DexFileClazz = appClassloader.loadClass("dalvik.system.DexFile");
} catch (Exception e) {
e.printStackTrace();
}
Method getClassNameList_method = null;
Method defineClass_method = null;
Method dumpDexFile_method = null;
Method dumpMethodCode_method = null;
for (Method field : DexFileClazz.getDeclaredMethods()) {
if (field.getName().equals("getClassNameList")) {
getClassNameList_method = field;
getClassNameList_method.setAccessible(true);
}
if (field.getName().equals("defineClassNative")) {
defineClass_method = field;
defineClass_method.setAccessible(true);
}
if (field.getName().equals("dumpMethodCode")) {
dumpMethodCode_method = field;
dumpMethodCode_method.setAccessible(true);
}
}
Field mCookiefield = getClassField(appClassloader, "dalvik.system.DexFile", "mCookie");
for (int j = 0; j < ElementsArray.length; j++) {
Object element = ElementsArray[j];
Object dexfile = null;
try {
dexfile = (Object) dexFile_fileField.get(element);
} catch (Exception e) {
e.printStackTrace();
}
if (dexfile == null) {
continue;
}
if (dexfile != null) {
dexFilesArray.add(dexfile);
Object mcookie = getClassFieldObject(appClassloader, "dalvik.system.DexFile", dexfile, "mCookie");
if (mcookie == null) {
continue;
}
String[] classnames = null;
try {
classnames = (String[]) getClassNameList_method.invoke(dexfile, mcookie);
} catch (Exception e) {
e.printStackTrace();
continue;
} catch (Error e) {
e.printStackTrace();
continue;
}
if (classnames != null) {
for (String eachclassname : classnames) {
loadClassAndInvoke(appClassloader, eachclassname, dumpMethodCode_method);
}
}
}
}
return;
}
這一串代碼主要是拿到DexFile的Class讯沈,拿到所有dex的引用汇在,循環(huán)遍歷出className浓恳,然后傳給loadClassAndInvoke方法。
public static void loadClassAndInvoke(ClassLoader appClassloader, String eachclassname, Method dumpMethodCode_method) {
Log.i("ActivityThread", "go into loadClassAndInvoke->" + "classname:" + eachclassname);
Class resultclass = null;
try {
resultclass = appClassloader.loadClass(eachclassname);
} catch (Exception e) {
e.printStackTrace();
return;
} catch (Error e) {
e.printStackTrace();
return;
}
if (resultclass != null) {
try {
Constructor<?> cons[] = resultclass.getDeclaredConstructors();
for (Constructor<?> constructor : cons) {
if (dumpMethodCode_method != null) {
try {
dumpMethodCode_method.invoke(null, constructor);
} catch (Exception e) {
e.printStackTrace();
continue;
} catch (Error e) {
e.printStackTrace();
continue;
}
} else {
Log.e("ActivityThread", "dumpMethodCode_method is null ");
}
}
} catch (Exception e) {
e.printStackTrace();
} catch (Error e) {
e.printStackTrace();
}
try {
Method[] methods = resultclass.getDeclaredMethods();
if (methods != null) {
for (Method m : methods) {
if (dumpMethodCode_method != null) {
try {
dumpMethodCode_method.invoke(null, m);
} catch (Exception e) {
e.printStackTrace();
continue;
} catch (Error e) {
e.printStackTrace();
continue;
}
} else {
Log.e("ActivityThread", "dumpMethodCode_method is null ");
}
}
}
} catch (Exception e) {
e.printStackTrace();
} catch (Error e) {
e.printStackTrace();
}
}
}
在這個方法中笛谦,會不斷調用dumpMethodCode這個方法抱虐,傳入構造函數(shù)與類中的方法。
而這個方法是DexFile中自定義添加的方法
static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method) {
ScopedFastNativeObjectAccess soa(env);
if(method!=nullptr)
{
ArtMethod* artmethod = ArtMethod::FromReflectedMethod(soa, method);
myfartInvoke(artmethod);
}
return;
}
ScopedObjectAccess的變體饥脑,它沒有可運行的過渡恳邀。只能由“快速”使用
JNI方法懦冰。
ScopedFastNativeObjectAccess 。從翻譯來講谣沸,這東西只能用于fastJni模式中刷钢,暫時忽略這個方法
重點是下面的通過FromReflectedMethod方法拿到ArtMethod后,就調用myfartInvoke方法
art_method.cc
extern "C" void myfartInvoke(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(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");
}
其中Thread指針是空
在Invoke中判斷到是空即為自己構造的調用乳附,
extern "C" void dumpArtMethod(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
if (dexfilepath == nullptr) {
LOG(INFO) <<
"ArtMethod::dumpArtMethodinvoked,methodname:"
<< PrettyMethod(artmethod).
c_str() << "malloc 2000 byte failed";
return;
}
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) {
read(fcmdline, szProcName, 256);
close(fcmdline);
}
if (szProcName[0]) {
const DexFile *dex_file = artmethod->GetDexFile();
const char *methodname =
PrettyMethod(artmethod).c_str();
const uint8_t *begin_ = dex_file->Begin();
size_t size_ = dex_file->Size();
memset(dexfilepath, 0, 2000);
int size_int_ = (int) size_;
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "%s", "/sdcard/fart");
mkdir(dexfilepath, 0777);
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "/sdcard/fart/%s",
szProcName);
mkdir(dexfilepath, 0777);
memset(dexfilepath, 0, 2000);
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 {
dexfilefp =
open(dexfilepath, O_CREAT | O_RDWR,
0666);
if (dexfilefp > 0) {
write(dexfilefp, (void *) begin_,
size_);
fsync(dexfilefp);
close(dexfilefp);
}
}
const DexFile::CodeItem * code_item =
artmethod->GetCodeItem();
if (LIKELY(code_item != nullptr)) {
int code_item_len = 0;
uint8_t *item = (uint8_t *) code_item;
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;
}
memset(dexfilepath, 0, 2000);
int size_int = (int) dex_file->Size(); // Length of data
uint32_t method_idx =
artmethod->get_method_idx();
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_%ld.bin",
szProcName, size_int, gettidv1());
int fp2 =
open(dexfilepath,
O_CREAT | O_APPEND | O_RDWR,
0666);
if (fp2 > 0) {
lseek(fp2, 0, SEEK_END);
memset(dexfilepath, 0, 2000);
int offset = (int) (item - begin_);
sprintf(dexfilepath,
"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",
methodname, method_idx,
offset, code_item_len);
int contentlength = 0;
while (dexfilepath[contentlength]
!= 0)
contentlength++;
write(fp2, (void *) dexfilepath,
contentlength);
long outlen = 0;
char *base64result =
base64_encode((char *) item,
(long)
code_item_len,
&outlen);
write(fp2, base64result, outlen);
write(fp2, "};", 2);
fsync(fp2);
close(fp2);
if (base64result != nullptr) {
free(base64result);
base64result = nullptr;
}
}
}
}
if (dexfilepath != nullptr) {
free(dexfilepath);
dexfilepath = nullptr;
}
}
這里代碼主要是根據(jù)artmethod的base和length 把dex dump出來
然后再獲取codeItem内地。
這里codeitem分兩種,帶try和不帶try赋除,因此要區(qū)分不同阱缓,不然dump下來的指令可能不全
讀取一個無符號的LEB128值,將給定的指針更新為point
剛好超過讀取值的末尾举农。這個功能可以容忍
第五個編碼字節(jié)中的非零高階位荆针。
這個地方的codeitem_end不知道是計算個啥,雖然知道最后結果是計算出末尾的地址并蝗,然后減去開頭就是長度祭犯,但是這個計算過程還是不是很了解
uint8_t *codeitem_end(const uint8_t ** pData) {
uint32_t num_of_list = DecodeUnsignedLeb128(pData);
for (; num_of_list > 0; num_of_list--) {
int32_t num_of_handlers =
DecodeSignedLeb128(pData);
int num = num_of_handlers;
if (num_of_handlers <= 0) {
num = -num_of_handlers;
}
for (; num > 0; num--) {
DecodeUnsignedLeb128(pData);
DecodeUnsignedLeb128(pData);
}
if (num_of_handlers <= 0) {
DecodeUnsignedLeb128(pData);
}
}
return (uint8_t *) (*pData);
}
雖然不懂是怎么計算的,但是通過搜索可以發(fā)現(xiàn)
dexhunter滚停,fupk3中也用到了這段代碼沃粗。總之就是計算出了codeitem的大小键畴。
這段中的128是編碼方式
https://blog.csdn.net/new_abc/article/details/36412081
初次之外最盅,還有一個方法是dumpDex的
extern "C" void dumpDexFileByExecute(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
if (dexfilepath == nullptr) {
LOG(INFO) <<
"ArtMethod::dumpDexFileByExecute,methodname:"
<< PrettyMethod(artmethod).
c_str() << "malloc 2000 byte failed";
return;
}
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) {
read(fcmdline, szProcName, 256);
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, 2000);
int size_int_ = (int) size_;
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "%s", "/sdcard/fart");
mkdir(dexfilepath, 0777);
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "/sdcard/fart/%s",
szProcName);
mkdir(dexfilepath, 0777);
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_dexfile_execute.dex",
szProcName, size_int_);
int dexfilefp = open(dexfilepath, O_RDONLY, 0666);
if (dexfilefp > 0) {
close(dexfilefp);
dexfilefp = 0;
} else {
dexfilefp =
open(dexfilepath, O_CREAT | O_RDWR,
0666);
if (dexfilefp > 0) {
write(dexfilefp, (void *) begin_,
size_);
fsync(dexfilefp);
close(dexfilefp);
}
}
}
if (dexfilepath != nullptr) {
free(dexfilepath);
dexfilepath = nullptr;
}
}
在解釋模式下,如果判斷到有初始化函數(shù)起惕,也就是init涡贱,即構造函數(shù)。就dempdex惹想。而在activityThread中问词,我們剛好就調用了構造函數(shù)的主動調用鏈
整個fart的調用鏈就是大概是這樣的。這是我的見解