欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Andrdoid中對應用程序的行為攔截實(shí)現方式之
之前的一篇概要文章中主要說(shuō)了我這次研究的一些具體情況,這里就不在多說(shuō)了,但是這里還需要指出的是,感謝一下三位大神愿意分享的知識(在我看來(lái),懂得分享和細致的人才算是大神,不一定是技術(shù)牛奧~~)
第一篇:http://blog.csdn.net/jinzhuojun/article/details/9900105
第二篇:http://bbs.pediy.com/showthread.php?t=186880
第三篇:http://bbs.pediy.com/showthread.php?t=157419
最重要的還是第一篇,所以這里我就不多介紹了,當然如果要看這篇blog的話(huà),最好是先仔細閱讀一下上面的三篇文章。
當然我對第一篇文章做了修改了詳細的描述了:http://blog.csdn.net/jiangwei0910410003/article/details/39293635
這篇文章最好是看懂了,而且是必須真的懂了,同時(shí)將demo自己執行一邊,流程走通了,不然下面也是會(huì )遇到問(wèn)題的。
當然這種攔截方式的前提是:手機必須root,同時(shí)需要獲取su權限
下面開(kāi)始進(jìn)入正題
當然在之前的文章中的摘要中我們可以看到我這次主要攔截的是可以獲取系統信息的進(jìn)程,所以我們要搞清楚攔截的對象,這里就不做介紹了,我們攔截的進(jìn)程是system_server(關(guān)于這個(gè)進(jìn)程可以google一下,當然我們可以使用adb shell以及ps命令查看這個(gè)進(jìn)程的相關(guān)信息)
關(guān)于inject源碼這里就不做太多的解釋了,主要來(lái)看一下他的main函數:
1
2
3
4
5
6
7
8
9
10
11
int main(int argc, char** argv) {
pid_t target_pid;
target_pid = find_pid_of("system_server");
if (-1 == target_pid) {
printf("Can't find the process\n");
return -1;
}
printf("target_id:%d\n",target_pid);
inject_remote_process(target_pid, "/data/libsys.so", "hook_entry",  "I'm parameter!", strlen("I'm parameter!"));
return 0;
}
這里的一個(gè)主要的方法就是inject_remote_process(...)
第一個(gè)參數:需要注入的進(jìn)程id
第二個(gè)參數:需要注入的動(dòng)態(tài)庫(其實(shí)這個(gè)庫中就是包含我們需要替換的函數地址)
第三個(gè)參數:動(dòng)態(tài)庫入口的函數名稱(chēng)
第四個(gè)參數:動(dòng)態(tài)庫入口的函數所需要的參數
第五個(gè)參數:動(dòng)態(tài)庫入口的函數所需要的參數的長(cháng)度
當然這里我們還有一個(gè)是通過(guò)進(jìn)程名稱(chēng)獲取到進(jìn)程的id的函數find_pid_of(...)
既然現在我們需要注入system_server進(jìn)程中,那么我們就要需要將我們的代碼注入到libbiner.so文件中
在sys.c代碼中修改基礎地方:
第一個(gè)修改的地方就是so文件路徑:
1
#define LIBSF_PATH  "/system/lib/libbinder.so"
然后就是注入的函數:我們記得注入surfaceflinger進(jìn)程的時(shí)候,攔截的是eglSwapBuffers函數,我們注入到system_server的話(huà),就是攔截ioctl函數,因為我們知道想使用一些系統服務(wù)都是調用這個(gè)方法的,下面就對這個(gè)函數進(jìn)行替換:
1
2
3
4
5
6
7
8
9
10
11
12
13
int (*old_ioctl) (int __fd, unsigned long int __request, void * arg) = 0;
// 欲接替ioctl的新函數地址,其中內部調用了老的ioctl
int new_ioctl (int __fd, unsigned long int __request, void * arg)
{
if ( __request == BINDER_WRITE_READ )
{
call_count++;
LOGD("call_count:%d",call_count);
}
int res = (*old_ioctl)(__fd, __request, arg);
return res;
}
在這個(gè)函數中,我們會(huì )判斷一下請求狀態(tài)_request,如果是BINDER_WRITE_READ,說(shuō)明上層有請求服務(wù)了,這里就是做一個(gè)簡(jiǎn)單的判斷,通過(guò)一個(gè)int值,然后用log將其值打印出來(lái)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
int hook_eglSwapBuffers()
{
old_ioctl = ioctl;
void * base_addr = get_module_base(getpid(), LIBSF_PATH);
LOGD("libsurfaceflinger.so address = %p\n", base_addr);
int fd;
fd = open(LIBSF_PATH, O_RDONLY);
if (-1 == fd) {
LOGD("error\n");
return -1;
}
Elf32_Ehdr ehdr;
read(fd, &ehdr, sizeof(Elf32_Ehdr));
unsigned long shdr_addr = ehdr.e_shoff;
int shnum = ehdr.e_shnum;
int shent_size = ehdr.e_shentsize;
unsigned long stridx = ehdr.e_shstrndx;
Elf32_Shdr shdr;
lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET);
read(fd, &shdr, shent_size);
char * string_table = (char *)malloc(shdr.sh_size);
lseek(fd, shdr.sh_offset, SEEK_SET);
read(fd, string_table, shdr.sh_size);
lseek(fd, shdr_addr, SEEK_SET);
int i;
uint32_t out_addr = 0;
uint32_t out_size = 0;
uint32_t got_item = 0;
int32_t got_found = 0;
for (i = 0; i < shnum; i++) {
read(fd, &shdr, shent_size);
if (shdr.sh_type == SHT_PROGBITS) {
int name_idx = shdr.sh_name;
if (strcmp(&(string_table[name_idx]), ".got.plt") == 0 || strcmp(&(string_table[name_idx]), ".got") == 0) {
out_addr = base_addr + shdr.sh_addr;
out_size = shdr.sh_size;
LOGD("out_addr = %lx, out_size = %lx\n", out_addr, out_size);
for (i = 0; i < out_size; i += 4) {
got_item = *(uint32_t *)(out_addr + i);
if (got_item  == old_ioctl) {
LOGD("Found eglSwapBuffers in got\n");
got_found = 1;
uint32_t page_size = getpagesize();
uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1));
mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
*(uint32_t *)(out_addr + i) = new_ioctl;
break;
} else if (got_item == new_ioctl) {
LOGD("Already hooked\n");
break;
}
}
if (got_found)
break;
}
}
}
free(string_table);
close(fd);
}
還有就是hook_eglSwapBuffers函數(函數名都難得改了~~)
好的,修改差不多了,下面我們來(lái)編譯吧~~
編譯會(huì )出錯的,因為會(huì )提示找不到ioctl的定義以及一些常量值,所以我們得找到這個(gè)函數的定義,百度一下之后會(huì )發(fā)現這個(gè)函數的定義是在binder.h中,當然這個(gè)頭文件我們是可以在A(yíng)ndroid源碼中找到的
(關(guān)于源碼下載和編譯的問(wèn)題:http://blog.csdn.net/jiangwei0910410003/article/details/37988637)。
然后將這個(gè)binder.h拷貝到我們編譯的目錄中,然后再代碼中引用一下即可。
再次編譯,擦,還是提示錯誤,說(shuō)這個(gè)函數沒(méi)有定義。
原因很簡(jiǎn)單,我們只是引用了頭文件,并沒(méi)有將函數的具體實(shí)現引用進(jìn)來(lái),所以還需要去找到這個(gè)函數的具體定義了。
這個(gè)過(guò)程就是有點(diǎn)麻煩了,糾結了很長(cháng)時(shí)間呀~~,幸好最后搞定了
具體步驟:
從設備的system/lib/ 目錄中找到libbinder.so文件,將其拷貝出來(lái),這是一個(gè)動(dòng)態(tài)庫文件
然后將其放到我們之前的NDK配置目錄中的:具體目錄如下:
最后我們在A(yíng)ndroid.mk文件進(jìn)行引用:
1
LOCAL_LDLIBS := -llog -lbinder -lutils -landroid_runtime
當然,這里我們會(huì )看到-lXXX是通用的格式,同樣的,我們如果要用到JVM中的函數的話(huà),會(huì )用到libandroid_runtime.so文件,頭文件:android_runtime.h,也是可以在源碼中找到的(后面會(huì )提及到)
(注:這里就介紹了我們如何在使用Android系統中底層的一些函數,同時(shí)編譯的時(shí)候引用到了動(dòng)態(tài)鏈接庫文件)
擴展:
這里在擴展一下:還有另外的一種方式引用so文件:
操作步驟:
首先在我們編譯目錄中新建一個(gè)文件夾:prebuilt
在這個(gè)文件夾中存放兩個(gè)文件:
1.Android.mk:
1
2
3
4
5
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := binder
LOCAL_SRC_FILES := libbinder.so
include $(PREBUILT_SHARED_LIBRARY)
2. libbinder.so(這個(gè)動(dòng)態(tài)鏈接庫文件就是我們需要用到的)
然后在回到我們編譯的目錄中,修改一下我們的Android.mk文件:
1
2
3
4
5
6
7
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE := sys
LOCAL_SRC_FILES := sys.c
include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/prebuilt/Android.mk
最重要的一行就是在最后。所以這也是一種方法。當然我們會(huì )看到這種方法有一個(gè)弊端,就是一次只能引用到一個(gè)so文件。所以使用范圍比較窄~~
在回到主題上來(lái),我們通過(guò)引用libbinder.so文件之后,編譯可以通過(guò)了,然后將編譯之后的libsys.so文件拷貝到/data/目錄中,這個(gè)和之前的libsuf.so步驟差不多。
然后進(jìn)入到設備的data目錄中,執行inject
=>adb shell
=>cd data
=>./inject
同時(shí)檢測一下log信息:
adb logcat -s PERMISSIONINTERCEPTER
隨便動(dòng)動(dòng)手機,call_count就刷刷的變~~
至此,我們看到了,hook進(jìn)程和攔截ioctl函數成功~~
Demo下載地址:
http://download.csdn.net/detail/jiangwei0910410003/7930603
下面再來(lái)深入看一下,如何攔截哪些應用在使用系統服務(wù)呢?
首先,我們還是需要對上面我們自定義的new_ioctl函數中的邏輯進(jìn)行修改一下。
其實(shí)這里面就有一個(gè)很大的難度了(這個(gè)也正是底層C進(jìn)行攔截的一個(gè)弊端),需要分析數據格式,然后才能正確的進(jìn)行攔截。
下面來(lái)看一下具體的代碼吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 欲接替ioctl的新函數地址,其中內部調用了老的ioctl
int new_ioctl (int __fd, unsigned long int __request, void * arg)
{
if ( __request == BINDER_WRITE_READ )
{
int dir  =  _IOC_DIR(__request);   //根據命令獲取傳輸方向
int type =  _IOC_TYPE(__request);  //根據命令獲取類(lèi)型
int nr   =  _IOC_NR(__request);    //根據命令獲取類(lèi)型命令
int size =  _IOC_SIZE(__request);  //根據命令獲取傳輸數據大小
struct binder_write_read* tmp = (struct binder_write_read*) arg;
signed long write_size = tmp->write_size;
signed long read_size = tmp->read_size;
if(write_size > 0)//該命令將write_buffer中的數據寫(xiě)入到binder
{
//LOGD("binder_write_read----->write size: %d,write_consumed :%d", tmp->write_size, tmp->write_consumed);
int already_got_size = 0;
unsigned long *pcmd = 0;
//LOGD("=================write_buffer process start!");
while(already_got_size < write_size)//循環(huán)處理buffer中的每一個(gè)命令
{
pcmd = (unsigned long *)(tmp->write_buffer + already_got_size);    //指針后移
int code = pcmd[0];
//LOGD("pcmd: %x, already_got_size: %d", pcmd, already_got_size);
int dir  =  _IOC_DIR(code);   //根據命令獲取傳輸方向
int type =  _IOC_TYPE(code);  //根據命令獲取類(lèi)型
int nr   =  _IOC_NR(code);    //根據命令獲取類(lèi)型命令
int size =  _IOC_SIZE(code);  //根據命令獲取傳輸數據大小
//LOGD("cmdcode:%d, dir:%d, type:%c, nr:%d, size:%d\n", code, dir, type, nr, size);
struct binder_transaction_data* pdata = (struct binder_transaction_data*)(&pcmd[1]);
switch (code)
{
case BC_TRANSACTION:
if(pdata->sender_pid>5000){
//LOGD("code:%d",pdata->code);
//LOGD("name:%x",pdata->data.ptr.buffer);
//LOGD("pid: %d",pdata->sender_pid);
//char *pname = (char*)malloc(50*sizeof(char));
//cfgmng_get_taskname(pdata->sender_pid,pname);
//LOGD("pname: %s",pname);
//free(pname);
//hexdump(pdata->data.ptr.buffer, pdata->data_size);
}
parse_binder(pdata, 1);
break;
case BC_REPLY:
//LOGD("pid: %d, BC_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 1);
break;
default:
break;
}
already_got_size += (size+4);
}
//LOGD("=================write_buffer process end!");
}
if(read_size > 0)//從binder中讀取數據寫(xiě)入到read_buffer
{
//LOGD("binder_write_read----->read size: %d, read_consumed: %d", tmp->read_size, tmp->read_consumed);
int already_got_size = 0;
unsigned long *pret = 0;
//LOGD("=================read_buffer process start!");
while(already_got_size < read_size)//循環(huán)處理buffer中的每一個(gè)命令
{
pret = (unsigned long *)(tmp->read_buffer + already_got_size);    //指針后移
int code = pret[0];
//LOGD("pret: %x, already_got_size: %d", pret, already_got_size);
int dir  =  _IOC_DIR(code);   //根據命令獲取傳輸方向
int type =  _IOC_TYPE(code);  //根據命令獲取類(lèi)型
int nr   =  _IOC_NR(code);    //根據命令獲取類(lèi)型命令
int size =  _IOC_SIZE(code);  //根據命令獲取傳輸數據大小
//LOGD("retcode:%d, dir:%d, type:%c, nr:%d, size:%d\n", code, dir, type, nr, size);
struct binder_transaction_data* pdata = (struct binder_transaction_data*)(&pret[1]);
switch (code)
{
case BR_TRANSACTION:
if(pdata->sender_pid>5000){
//LOGD("code:%d",pdata->code);
//LOGD("name:%s",pdata->data.ptr.buffer->flag);
//LOGD("pid: %d",pdata->sender_pid);
//char *pname = (char*)malloc(50*sizeof(char));
//getNameByPid(pdata->sender_pid,pname);
//LOGD("pname: %s",pname);
//free(pname);
char * pname = hexdump(pdata->data.ptr.buffer, pdata->data_size);
if(isStub(pname,STUB) == 1 && pid!=pdata->sender_pid)
{
pid = pdata->sender_pid;
//指定一下log的輸出格式:服務(wù)的名稱(chēng)&&應用的進(jìn)程id
LOGD("%s&&%d",pname,pdata->sender_pid);
//在一個(gè)線(xiàn)程中開(kāi)啟socket
/*pthread_t tid;
int status = pthread_create(&tid,NULL,&new_socket,(void*)&data);
if(status != 0){
LOGD("can't create thread");
}else{
LOGD("create thread success");
}*/
//開(kāi)一個(gè)socket
//new_socket(pid,pname);
//采用動(dòng)態(tài)調用so文件中的函數f()
/*void * dp = dlopen("libmiddle.so",RTLD_NOW);
LOGD("dp is %p",dp);
int (*f)() = dlsym(dp,"f");
LOGD("func pointer==%p",f);
if(f == NULL)
{
LOGD("not find func");
}
else
{
(*f)();
}*/
}
free(pname);
}
parse_binder(pdata, 2);
break;
case BR_REPLY:
//LOGD("pid: %d, BR_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 2);
break;
default:
break;
}
already_got_size += (size+4);//數據內容加上命令碼
}
//LOGD("=================read_buffer process end!");
}
}
if (old_ioctl == -1)
{
//LOGD("error\n");
return;
}
int res = (*old_ioctl)(__fd, __request, arg);
return res;
}
這個(gè)函數修改的邏輯就比較多了~~
首先來(lái)了解一下Binder在傳輸數據中比較關(guān)鍵的一個(gè)數據結構:binder_transaction_data
了解了這個(gè)數據結構之后,我們就可以從這個(gè)數據結構中提取出我們想要的服務(wù)的名稱(chēng)(這一步真的很重要,也是網(wǎng)上很多人咨詢(xún)的一個(gè)問(wèn)題,關(guān)于這個(gè)問(wèn)題,我是閱讀了一本比較好的資料《Android框架揭秘》是棒子寫(xiě)的一本書(shū),里面有一些錯誤,但是不影響大體的知識方向)、同時(shí)也是可以獲取到進(jìn)程id,Android中一個(gè)應用一般就是一個(gè)進(jìn)程,所以我們可以粗略的通過(guò)進(jìn)程id來(lái)來(lái)獲取應用的一些詳細信息(可以獲取最近正在運行的應用信息列表中進(jìn)行過(guò)濾)
獲取服務(wù)的名稱(chēng)的主要操作:
1
char * pname = hexdump(pdata->data.ptr.buffer, pdata->data_size);
然后就是data.ptr.buffer的數據結構:
那么現在問(wèn)題就是如何能將這個(gè)服務(wù)的名稱(chēng)正確的取出來(lái)呢?那么這個(gè)就要看hexdump函數了<喎?"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD48cD6009fWw+bS4tLlyc+/tNOmuMPKx72raGV4zsSxvtequ6/Su8/Co7o8L3A+PHA+PC9wPjxwcmUgY2xhc3M9"brush:java;">char* hexdump(void *_data, unsigned len){ unsigned char *data = _data; char *dataAry = (char*)malloc(len*(sizeof(char))); char *dataTmp = dataAry; unsigned count; for (count = 0; count < len; count++) { if ((count & 15) == 0) LOGD(stderr,"%04x:", count); //only show charset and '.' if(((*data >= 65) && (*data <= 90)) || ((*data >= 97) && (*data <= 122)) || (*data == 46)) { *dataAry = *data; dataAry++; } data++; if ((count & 15) == 15) LOGD(stderr,"\n"); } *dataAry = '\0'; return dataTmp; if ((count & 15) != 0) LOGD(stderr,"\n");}
看到中間的一個(gè)核心的if判斷,那個(gè)就是用來(lái)過(guò)濾服務(wù)的包名的:大小寫(xiě)字母+點(diǎn)號,這樣就能從buffer中提取出服務(wù)的包名了,然后返回即可(說(shuō)實(shí)話(huà),這個(gè)問(wèn)題也是糾結了我好長(cháng)時(shí)間,解決了還是很開(kāi)心的)
在函數new_ioctl中最主要的核心代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
switch (code)
{
case BR_TRANSACTION:
if(pdata->sender_pid>5000){
//LOGD("pid: %d, BR_TRANSACTION, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
//LOGD("code:%d",pdata->code);
//LOGD("name:%s",pdata->data.ptr.buffer->flag);
//LOGD("pid: %d",pdata->sender_pid);
//char *pname = (char*)malloc(50*sizeof(char));
//getNameByPid(pdata->sender_pid,pname);
//LOGD("pname: %s",pname);
//free(pname);
char * pname = hexdump(pdata->data.ptr.buffer, pdata->data_size);
if(isStub(pname,STUB) == 1 && pid!=pdata->sender_pid)
{
pid = pdata->sender_pid;
//指定一下log的輸出格式:服務(wù)的名稱(chēng)&&應用的進(jìn)程id
LOGD("%s&&%d",pname,pdata->sender_pid);
//在一個(gè)線(xiàn)程中開(kāi)啟socket
/*pthread_t tid;
int status = pthread_create(&tid,NULL,&new_socket,(void*)&data);
if(status != 0){
LOGD("can't create thread");
}else{
LOGD("create thread success");
}*/
//開(kāi)一個(gè)socket
//new_socket(pid,pname);
//采用動(dòng)態(tài)調用so文件中的函數f()
/*void * dp = dlopen("libmiddle.so",RTLD_NOW);
LOGD("dp is %p",dp);
int (*f)() = dlsym(dp,"f");
LOGD("func pointer==%p",f);
if(f == NULL)
{
LOGD("not find func");
}
else
{
(*f)();
}*/
}
free(pname);
}
parse_binder(pdata, 2);
break;
case BR_REPLY:
//LOGD("pid: %d, BR_REPLY, dir:%d, type:%c, nr:%d, size:%d\n", pdata->sender_pid, dir, type, nr, size);
parse_binder(pdata, 2);
break;
default:
break;
}
這個(gè)函數中就是判斷請求的方式
當code等于BR_TRANSACTION
表示開(kāi)始讀取數據,所以我們這時(shí)候就可以在這里進(jìn)行數據的攔截分析,這里在獲取進(jìn)程id的時(shí)候,我做了一次判斷就是判斷進(jìn)程id大于5000的,這個(gè)5000只是我隨便取的一個(gè)值,因為如果這里不做判斷的話(huà),在后面打印log信息的時(shí)候很不方便,因為系統有很多應用都可能在獲取服務(wù),log信息有點(diǎn)多吧,這里就相當于做個(gè)過(guò)濾。同時(shí)這里還有一個(gè)過(guò)濾條件,就是服務(wù)名稱(chēng)中有ILocationManager的,因為開(kāi)始的時(shí)候只是想驗證一下這個(gè)不做能否成功,所以先用經(jīng)緯度信息來(lái)做實(shí)驗。
上面的攔截操作算是完成了,那么下面我們還得來(lái)做一件事(這件事也是網(wǎng)上好多同學(xué)糾結的一個(gè)問(wèn)題)
就是如何將我們這里攔截到的信息包括是哪個(gè)應用獲取服務(wù)的進(jìn)程id以及獲取服務(wù)的名稱(chēng),如何將其這些信息傳遞到上層然后進(jìn)行顯示(比如360彈出的對話(huà)框,或者是在通知欄中:XXX應用正在獲取你的XXX信息,拒絕還是允許)
我也花了一個(gè)禮拜的時(shí)間去解決的,找到三種方式:
第一種方式:通過(guò)上層(就是我們的app,建立一個(gè)SockeServer),然后在底層攔截到的詳細信息通過(guò)socket傳遞到server中。
底層的代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void new_socket(int pid,char *pname){
int sockfd,sendbytes;
char buf[MAXDATASIZE];
struct hostent *host = gethostbyname(SERVIP);
struct sockaddr_in serv_addr;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
LOGD("socket fail");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr.s_addr = inet_addr(SERVIP);
bzero(&(serv_addr.sin_zero),8);
if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
LOGD("connect fail");
//exit(1);
}
if((sendbytes=send(sockfd,pname,strlen(pname),0))==-1){
LOGD("send fail");
//exit(1);
}
free(pname);
close(sockfd);
}
這個(gè)是在Linux中建立一個(gè)Socket,具體的操操作這里就不做解釋了,自行可以去搜索一下Linux中如何建立socket。
下面在來(lái)看一下上層的app中建立一個(gè)SocketServer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
new Thread(){
@Override
public void run(){
java.net.ServerSocket sc = null;
try {
sc = new java.net.ServerSocket(3333);
Log.i("TTT","socket create success");
while (true) {
java.net.Socket client = sc.accept();
String content = streamToString(client.getInputStream());
Log.i("TTT", "accept ==> "+content);
}
} catch (Exception ef) {
Log.i("TTT", "exception===>"+ef.getMessage());
}finally{
Log.i("TTT", "stop connection");
if (sc != null){
try {
sc.close();
} catch (IOException e) {
Log.i("TTT", "exception===>"+e.getMessage());
}
sc = null;
}
}
}
}.start();
這段代碼一般是放在Service中的,因為要始終監聽(tīng),同時(shí)這里開(kāi)啟的端口是:3333.為了防止和其他應用的端口沖突,一邊最好起個(gè)比較有個(gè)性的端口,當然這里沒(méi)有做更好的優(yōu)化,比如當建立端口的時(shí)候發(fā)現這個(gè)端口已經(jīng)被占用了怎么辦?做的好的應該做一下端口的判斷,然后找到?jīng)]被占用的端口(可以用Socket探針技術(shù))。
第二種方式:有點(diǎn)復雜了就是使用JNI技術(shù)了,我們需要在底層中調用上層的Java代碼。這個(gè)技術(shù)就是可以在C++代碼中使用類(lèi)加載機制+反射技術(shù)調用Java中的指定方法。這里有一個(gè)問(wèn)題就是如何獲取JVM對象以及JNIEnv對象。
眾所周知,Android的應用進(jìn)程,都是由Zygote孵化的子進(jìn)程,每個(gè)進(jìn)程都運行在獨立的JVM中(具體的知識可以查看老羅的blog)。
那么JVM對象可以獲取到,那么JNIEnv對象呢?我們知道,在JVM進(jìn)程中,JavaVM是全局唯一的,而JNIEnv則是按線(xiàn)程分配。另外,Dalvik的線(xiàn)程跟Linux線(xiàn)程是一一對應的,因此我們可以把自身所在的線(xiàn)程Attatch到JavaVM,JavaVM就會(huì )為我們分配JNIEnv對象了。通過(guò)閱讀Dalvik源碼,從AndroidRuntime類(lèi)中我們可以得到JavaVm的地址,再通過(guò)JavaVm所提供的AttachCurrentThead和DetachCurrentThread兩個(gè)函數,即可完成JNIEnv的獲取。
具體代碼如下:
importdex.cpp代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <stddef.h>
#include <jni.h>
#include
#include "log.h"
using namespace android;
static const char JSTRING[] = "Ljava/lang/String;";
static const char JCLASS_LOADER[] = "Ljava/lang/ClassLoader;";
static const char JCLASS[] = "Ljava/lang/Class;";
static JNIEnv* jni_env;
static char sig_buffer[512];
//ClassLoader.getSystemClassLoader()
static jobject getSystemClassLoader(){
jclass class_loader_claxx = jni_env->FindClass("java/lang/ClassLoader");
snprintf(sig_buffer, 512, "()%s", JCLASS_LOADER);
jmethodID getSystemClassLoader_method = jni_env->GetStaticMethodID(class_loader_claxx, "getSystemClassLoader", sig_buffer);
return jni_env->CallStaticObjectMethod(class_loader_claxx, getSystemClassLoader_method);
}
void Main() {
JavaVM* jvm = AndroidRuntime::getJavaVM();
jvm->AttachCurrentThread(&jni_env, NULL);
//TODO 使用JNIEnv
//jvm->DetachCurrentThread();
jstring apk_path = jni_env->NewStringUTF("/data/local/tmp/DemoInject2.apk");
jstring dex_out_path = jni_env->NewStringUTF("/data/data/");
jclass dexloader_claxx = jni_env->FindClass("dalvik/system/DexClassLoader");
snprintf(sig_buffer, 512, "(%s%s%s%s)V", JSTRING, JSTRING, JSTRING, JCLASS_LOADER);
jmethodID dexloader_init_method = jni_env->GetMethodID(dexloader_claxx, "<init>", sig_buffer);
snprintf(sig_buffer, 512, "(%s)%s", JSTRING, JCLASS);
jmethodID loadClass_method = jni_env->GetMethodID(dexloader_claxx, "loadClass", sig_buffer);
jobject class_loader = getSystemClassLoader();
check_value(class_loader);
jobject dex_loader_obj = jni_env->NewObject(
dexloader_claxx, dexloader_init_method, apk_path, dex_out_path, NULL, class_loader);
jstring class_name = jni_env->NewStringUTF("com.demo.inject2.EntryClass");
jclass entry_class = static_cast<jclass>(jni_env->CallObjectMethod(dex_loader_obj, loadClass_method, class_name));
jmethodID invoke_method = jni_env->GetStaticMethodID(entry_class, "invoke", "(I)[Ljava/lang/Object;");
check_value(invoke_method);
jobjectArray objectarray = (jobjectArray) jni_env->CallStaticObjectMethod(entry_class, invoke_method, 0);
jvm->DetachCurrentThread();
}</jclass></init></android_runtime></jni.h></stddef.h></stdio.h>
這里會(huì )遇到一個(gè)問(wèn)題就是AndroidRuntime類(lèi)找不到的問(wèn)題,可以google一下,他的定義在android_runtime.h頭文件中,這個(gè)同樣也是可以從Android源碼中找到的,同時(shí)這個(gè)AndroidRuntime類(lèi)的實(shí)現可以從設備的/system/lib/目錄中找到動(dòng)態(tài)庫文件libandroid_runtime.so。和之前使用libbinder.so差不多的步驟,這里就不做說(shuō)明了。
編譯項目下載地址:
http://download.csdn.net/detail/jiangwei0910410003/7932355
編譯之后得到importdex.so文件,這個(gè)在后面會(huì )用到。
下面來(lái)具體看一下代碼。在看代碼之前需要先了解一下Android中的類(lèi)加載機制,請看下面的一篇文章:
http://blog.csdn.net/jiangwei0910410003/article/details/17679823
如果這篇文章看懂了,其實(shí)上面的代碼就沒(méi)有任何難度了,其實(shí)就是C++中實(shí)現這種方式,和Java中的反射機制很相似,
在代碼中還可以看到DexClassLoader這個(gè)類(lèi)的一個(gè)參數是dex文件的輸出目錄。這里直接放到了/data/data目錄中。
同時(shí)我們需要將開(kāi)發(fā)一個(gè)DemoInject2.apk,這里面要有一個(gè)類(lèi):com.demo.inject2.EntryClass,在這個(gè)類(lèi)中需要定義一個(gè)靜態(tài)的invoke方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.demo.inject2;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
public final class EntryClass {
public static Object[] invoke(int i) {
try {
Log.i("TTT", ">>>>>>>>>>>>>I am in, I am a bad boy 2!!!!<<<<<<<<<<<<<<");
Context context = ContexHunter.getContext();
Toast.makeText(context, "Success", Toast.LENGTH_LONG).show();
/*Class<!--?--> MainActivity_class = context.getClassLoader().loadClass("com.demo.host.MainActivity");
Method setA_method = MainActivity_class.getDeclaredMethod("setA", int.class);
setA_method.invoke(null, 1);*/
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
然后我們就可以通過(guò)底層調用這個(gè)方法,將進(jìn)程id和服務(wù)的名稱(chēng)作為參數傳遞過(guò)去,但是在使用這種方法的時(shí)候遇到一個(gè)問(wèn)題:
那就是我們看到sys.c是C程序,但是上面的那個(gè)程序是C++的,所以這里面會(huì )牽扯到一個(gè)問(wèn)題就是怎么在C程序中調用C++的代碼,這個(gè)可以看一下下面的一篇文章是怎么操作:
http://blog.csdn.net/jiangwei0910410003/article/details/39312947
下面看看我們這里是如何做到的
這里會(huì )用到一個(gè)libimportdex.so文件(上面編譯之后的文件),還需要在弄一個(gè)編譯項目middle.
middle文件夾:
middle.c:
1
2
3
4
5
6
7
8
9
#include "importdex.h"
extern "C"{
int f()
{
callback();
return 0;
}
}
Android.mk:
1
2
3
4
5
6
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog -lbinder -lutils -limportdex
LOCAL_MODULE := middle
LOCAL_SRC_FILES := middle.cpp
include $(BUILD_SHARED_LIBRARY)
這里我們還需要將之前的編譯得到的libimportdex.so文件拷貝到NDK的配置目錄中
編譯項目下載地址:
http://download.csdn.net/detail/jiangwei0910410003/7932369
底層的執行so文件中指定的函數的核心代碼:
1
2
3
4
5
6
7
8
9
10
11
12
void * dp = dlopen("libmiddle.so",RTLD_NOW);
LOGD("dp is %p",dp);
int (*f)() = dlsym(dp,"f");
LOGD("func pointer==%p",f);
if(f == NULL)
{
LOGD("not find func");
}
else
{
(*f)();
}
最后如果想成功的話(huà),還需要將importdex.so文件以及l(fā)ibmiddle.so文件拷貝到設備的/system/lib/目錄下面,因為你需要引用。
第三種方式:使用Log日志來(lái)傳遞數據(這個(gè)是最終的解決方法)
這個(gè)實(shí)現原理很簡(jiǎn)單,就是在底層C中,我們拿到的了進(jìn)程的id和服務(wù)的名稱(chēng)然后使用特定的規則將其進(jìn)行拼接,然后在上層的app進(jìn)行log的攔截,然后獲取指定的值,這里會(huì )遇到一個(gè)問(wèn)題就是如何能在本應用中獲取其他進(jìn)程中的log信息,如果我們在本應用中獲取log信息很簡(jiǎn)單的,但是獲取其他進(jìn)程的log信息遇到點(diǎn)問(wèn)題,但是最后靠著(zhù)自己摸索,發(fā)現在su權限的情況下,可以獲取到所有進(jìn)程的log信息,所以我們要獲取su權限。
在上層代碼中取指定log信息代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.isoft.log;
import java.io.DataInputStream;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
public class LogObserverService extends Service implements Runnable{
private String TAG = "LOG";
private StringBuffer logContent = null;
private final static String[] CMDS = new String[]{"/system/bin/su","-c","logcat -s PERMISSIONINTERCEPTER"};
@SuppressLint("HandlerLeak")
private Handler handler = new Handler(){
@Override
public void dispatchMessage(Message msg) {
if(msg.what == 0){
String content = (String)msg.obj;
Toast.makeText(getApplicationContext(), "conent:"+content, Toast.LENGTH_LONG).show();
}
super.dispatchMessage(msg);
}
};
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"onCreate");
logContent = new StringBuffer();
new Thread(this).start();
}
@Override
public void run() {
Process pro = null;
try {
pro = Runtime.getRuntime().exec(CMDS);
}catch (Exception e) {
Log.i(TAG, "異常:"+e.getMessage());
e.printStackTrace();
}
DataInputStream dis = new DataInputStream(pro.getInputStream());
String line = null;
while (true) {
try {
while ((line = dis.readLine()) != null) {
if(line != null){
String[] lineAry = line.split("&&");
if(lineAry.length == 2){
//檢測一下獲取有位置信息的log信息
if(lineAry[0].contains("ILocationManager")){
Message msg = new Message();
msg.what = 0;
msg.obj = "進(jìn)程id:"+lineAry[1] + "\n獲取位置信息";
handler.sendMessage(msg);
}
}
}
String temp = logContent.toString();
logContent.delete(0, logContent.length());
logContent.append(line);
logContent.append("\n");
logContent.append(temp);
Thread.yield();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
主要的執行命令:
1
private final static String[] CMDS = new String[]{"/system/bin/su","-c","logcat -s PERMISSIONINTERCEPTER"};
這樣就可以獲取到指定的Tag的log信息,然后對其進(jìn)行解析,獲取進(jìn)程id和服務(wù)的名稱(chēng)。
這三種方式的實(shí)現的代碼的Demo下載地址(前兩種方式我在代碼中注釋了,如果想用的話(huà),可以打開(kāi)注釋)
總結一下,為什么會(huì )有這三種方式的產(chǎn)生呢?首先在有這種需求,想到的解決方式就是第一種。但是我在實(shí)現第一種方式的時(shí)候,出現了一個(gè)問(wèn)題,就是運行一段時(shí)間總是會(huì )死機,總是需要reboot命令重啟或者扣電池了~~,現在還沒(méi)找到解決方案。所以就延伸出第二種方式了,但是第二種方式可以是可以,沒(méi)先到也會(huì )死機和第一種方式的問(wèn)題一樣?,F在還沒(méi)有找到解決方案。所以就延伸出第三種方式了,因為第三種方式真的很簡(jiǎn)單。但是第三種方式有一個(gè)很大的問(wèn)題,就是不能和底層進(jìn)行交互,因為當我們在上層app中會(huì )點(diǎn)擊拒絕的話(huà),就是不給某個(gè)應用獲取此服務(wù),所以我們需要得到用戶(hù)的選擇狀態(tài)值,這樣才能在底層做具體的操作?;叵胍幌?,第一種和第二種方式都是可以實(shí)現的。但是會(huì )出現死機的情況。所以這個(gè)在后續是一定要解決的。
最后在來(lái)感受一下效果吧:
(ps:關(guān)于這個(gè)注入和攔截的問(wèn)題,網(wǎng)上有很多同學(xué)都在研究,可能有的同學(xué)已經(jīng)成功了,但是有的同學(xué)沒(méi)有,所以我就將這篇文章分享了一下,因為在這個(gè)過(guò)程中,我懂得那種遇到問(wèn)題無(wú)助的感觸,所以如果你看完了這個(gè)文章,如果遇到任何問(wèn)題,請給我留言,我能幫你解決的盡量解決一下)
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Android中的so注入(inject)和掛鉤(hook)
Android下引用系統庫的方法及問(wèn)題
解讀Android LOG機制的實(shí)現:(6)c/c 域使用LOG
把串口接收的數據送往應用層的方法
治標治本,徹底解決AVR單片機EEPROM數據丟失問(wèn)題
android的binder機制研究
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久