@oro-oro
2015-09-17T09:59:31.000000Z
字数 10423
阅读 2888
DroidHOOK
准备一个发短信的DEMO。
package com.test.smsdemo;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.telephony.SmsManager;import android.view.View;import android.widget.Button;import android.widget.EditText;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final EditText numText = (EditText) findViewById(R.id.num);final EditText contentText = (EditText) findViewById(R.id.content);Button button = (Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String num = numText.getText().toString();String content = contentText.getText().toString();SmsManager smsManager = SmsManager.getDefault();smsManager.sendTextMessage(num, null, content, null, null);}});}}
http://yunpan.cn/cmaY6L6iLtIDt 访问密码 ddf7
在 Android 2.3.3 平台下,不让SMSDemo发送短信。
发短信的操作并不是 APP 直接调用的,APP 会把发送请求发给 SmsService 服务进程,由这个服务进程再调用系统 API 来发短信。而 APP 要想获取 SmsService 服务,需要去 service_manager 查询这个服务的 ID,然后根据 ID 和相应的 service 利用 binder 进行通信。
考虑利用 ptrace 拦截 SMSDemo 请求系统 Service 的能力而不影响其他应用。
http://androidxref.com/2.3.7/xref/frameworks/base/cmds/servicemanager/service_manager.c
189int svcmgr_handler(struct binder_state *bs,190 struct binder_txn *txn,191 struct binder_io *msg,192 struct binder_io *reply)193{194 struct svcinfo *si;195 uint16_t *s;196 unsigned len;197 void *ptr;198 uint32_t strict_policy;199200// LOGI("target=%p code=%d pid=%d uid=%d\n",201// txn->target, txn->code, txn->sender_pid, txn->sender_euid);202203 if (txn->target != svcmgr_handle) // <<<<<----- 这里是关键 -----<<<<<204 return -1;205206 // Equivalent to Parcel::enforceInterface(), reading the RPC207 // header with the strict mode policy mask and the interface name.208 // Note that we ignore the strict_policy and don't propagate it209 // further (since we do no outbound RPCs anyway).210 strict_policy = bio_get_uint32(msg);211 s = bio_get_string16(msg, &len);212 if ((len != (sizeof(svcmgr_id) / 2)) ||213 memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {214 fprintf(stderr,"invalid id %s\n", str8(s));215 return -1;216 }217218 switch(txn->code) {219 case SVC_MGR_GET_SERVICE:220 case SVC_MGR_CHECK_SERVICE:221 s = bio_get_string16(msg, &len);222 ptr = do_find_service(bs, s, len);223 if (!ptr)224 break;225 bio_put_ref(reply, ptr);226 return 0;227228 case SVC_MGR_ADD_SERVICE:229 s = bio_get_string16(msg, &len);230 ptr = bio_get_ref(msg);231 if (do_add_service(bs, s, len, ptr, txn->sender_euid))232 return -1;233 break;234235 case SVC_MGR_LIST_SERVICES: {236 unsigned n = bio_get_uint32(msg);237238 si = svclist;239 while ((n-- > 0) && si)240 si = si->next;241 if (si) {242 bio_put_string16(reply, si->name);243 return 0;244 }245 return -1;246 }247 default:248 LOGE("unknown code %d\n", txn->code);249 return -1;250 }251252 bio_put_uint32(reply, 0);253 return 0;254}255
txn 是一个 binder_txn 结构体,通过txn->sender_euid可以获得用户 ID。
http://androidxref.com/2.3.7/xref/frameworks/base/cmds/servicemanager/binder.h
20struct binder_txn21{22 void *target; // 4个字节23 void *cookie; // 4个字节24 uint32_t code; // 4个字节25 uint32_t flags; // 4个字节2627 uint32_t sender_pid; // 4个字节28 uint32_t sender_euid;2930 uint32_t data_size;31 uint32_t offs_size;32 void *data;33 void *offs;34};35
Android 中每个 APP 都被分配了一个用户,利用用户来进行权限管理,用户分配表在 /data/system/packages.list。
查看SMSDemo的用户ID
# cat /data/system/packages.list...com.test.smsdemo 10037 1 /data/data/com.test.smsdemo...
观察215-216行,通过txn->sender_euid来判断是哪个 APP 在请求。这样就可以根据需要来屏蔽特定应用所有的服务请求了。
现在有两个选择:
写一个 tracer 程序,利用 ptrace attach 到 servicemanager 进程,在函数svcmgr_handler中添加断点。中断之后 tracer 根据txn->sender_euid的值来修改相应的寄存器或者内存值,达到改变程序流程的目的。这种做法需要 tracer 一直运行,处于调试 servicemanager 的状态,每次svcmgr_handler都需要从 tracer 过一次。如果 tracer 挂了会导致不可预料的结果。
写一个 tracer 程序,只运行一次,利用 ptrace attach 到 servicemanager 进程,在 text 段修改svcmgr_handler的逻辑并插入我们的代码,然后 dettach。这样的话 trace 不必长期运行,也不用每次调用都过 tracer,稳定性和效率都大大提高。
选择2。
现在需要修改的代码段是218-219:
if (txn->target != svcmgr_handle)return -1;
修改为:
if(txn->sender_euid == 10037) { // 刚才查到的 SMSDemo 的用户 IDtxn->target = 0;svcmgr_handle = 1; // 这样下面的if代码块,必定返回-1.}if (txn->target != svcmgr_handle)return -1;
接下来就写ShellCode了。
adb pull /system/bin/servicemanager 将servicemanager导出,拉进 IDA,通过字符串invalid id %s\n定位到svcmgr_handler函数。
.text:0000893C ; =============== S U B R O U T I N E =======================================.text:0000893C.text:0000893C.text:0000893C sub_893C ; DATA XREF: sub_8780+3Eo.text:0000893C ; .got:off_A15Co.text:0000893C.text:0000893C var_38 = -0x38.text:0000893C var_2C = -0x2C.text:0000893C s2 = -0x28.text:0000893C var_24 = -0x24.text:0000893C var_1C = -0x1C.text:0000893C.text:0000893C PUSH {R4-R7,LR}.text:0000893E LDR R5, =(_GLOBAL_OFFSET_TABLE_ - 0x894A).text:00008940 MOVS R4, R1 /* R4 = R1 = *txn */.text:00008942 LDR R1, =(off_A158 - 0xA170).text:00008944 SUB SP, SP, #0x24.text:00008946 ADD R5, PC ; _GLOBAL_OFFSET_TABLE_.text:00008948 STR R0, [SP,#0x38+var_24].text:0000894A LDR R0, [R5,R1] ; unk_A1F0.text:0000894C STR R3, [SP,#0x38+var_2C].text:0000894E MOVS R6, R2.text:00008950 LDR R3, [R0] /* 注入地址 */.text:00008952 LDR R2, [R4].text:00008954 CMP R2, R3.text:00008956 BNE loc_8A38 /* txn->target != svcmgr_handle */.text:00008958 MOVS R0, R6.text:0000895A ADD R7, SP, #0x38+var_1C.text:0000895C BL sub_8B78.text:00008960 MOVS R0, R6.text:00008962 MOVS R1, R7.text:00008964 BL sub_8BA4.text:00008968 LDR R2, [SP,#0x38+var_1C].text:0000896A STR R0, [SP,#0x38+s2].text:0000896C CMP R2, #0x1A.text:0000896E BNE loc_8980.text:00008970 LDR R3, =(__data_start_ptr - 0xA170).text:00008972 LDR R1, [SP,#0x38+s2] ; s2.text:00008974 MOVS R2, #0x34 ; n.text:00008976 LDR R0, [R5,R3] ; __data_start_ptr ; "android.os.IServiceManager".text:00008978 BLX memcmp.text:0000897C CMP R0, #0.text:0000897E BEQ loc_8998.text:00008980.text:00008980 loc_8980 ; CODE XREF: sub_893C+32j.text:00008980 LDR R0, [SP,#0x38+s2].text:00008982 BL sub_86E0.text:00008986 LDR R4, =(__sF_ptr - 0xA170).text:00008988 MOVS R2, R0.text:0000898A LDR R1, =(aInvalidIdS - 0x8992).text:0000898C LDR R0, [R5,R4] ; __sF.text:0000898E ADD R1, PC ; "invalid id %s\n".text:00008990 ADDS R0, #0xA8 ; stream.text:00008992 BLX fprintf.text:00008996 B loc_8A38
我们需要在.text:00008950之前,获得*txn->sender_euid,判断是否为10037,如果是,则修改 R2 和 R3 的值让其直接返回。
Shell Code 如下:
asm.s
push {r1,lr}push {r0-r7}LDR R7, [R4,#0x14] /* R4就是*txn,偏移20(0X14)个字节获得 euid */ldr r3,=10037CMP R7, R3pop {r0-r7}BEQ loc_ret_1LDR R3, [R0] /* 原来的指令 */LDR R2, [R4] /* 原来的指令 */pop {r1,pc}loc_ret_1:mov r3, #0mov r2, #1pop {r1,pc}
汇编:
$ as -ahlm -mthumb asm.sARM GAS asm.s page 11 0000 02B5 push {r1,lr}2 0002 FFB4 push {r0-r7}3 0004 6769 LDR R7, [R4,#0x14]4 0006 054B ldr r3,=100375 0008 9F42 CMP R7, R36 000a FFBC pop {r0-r7}7 000c 02D0 BEQ loc_ret_18 000e 0368 LDR R3, [R0]9 0010 2268 LDR R2, [R4]10 0012 02BD pop {r1,pc}11 loc_ret_1:12 0014 0023 mov r3, #013 0016 0122 mov r2, #114 0018 02BD0000 pop {r1,pc}14 35270000
源码:
#include <sys/ptrace.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <stdlib.h>#include <sys/syscall.h>int long_size = sizeof(long);void append_asm(char *asm_bin, int *len, unsigned long aasm, int alen){int i;char *aasmp = &aasm;for(i = 0; i < alen; i++){*(asm_bin + (*len)) = *(aasmp + i);(*len)++;}}void getdata(pid_t pid, long addr,char *str, int len){char *laddr;int i, j;union u{long val;char chars[long_size];} data;i = 0;j = len / long_size;laddr = str;while(i < j){data.val = ptrace(PTRACE_PEEKDATA,pid, addr + i * 4,NULL);memcpy(laddr, data.chars, long_size);++i;laddr += long_size;}j = len % long_size;if(j != 0){data.val = ptrace(PTRACE_PEEKDATA,pid, addr + i * 4,NULL);memcpy(laddr, data.chars, j);}str[len] = '\0';}void putdata(pid_t pid, long addr, char *str, int len){char *laddr;int i, j;union u{long val;char chars[long_size];} data;i = 0;j = len / long_size;laddr = str;while(i < j){memcpy(data.chars, laddr, long_size);ptrace(PTRACE_POKEDATA, pid,addr + i * 4, data.val);++i;laddr += long_size;}j = len % long_size;if(j != 0){memcpy(data.chars, laddr, j);ptrace(PTRACE_POKEDATA, pid,addr + i * 4, data.val);}}void build_jmp_asm(char *asm_bin, int *len, unsigned long freeaddr){append_asm(asm_bin, len, 0xFCA0F001, 4); //b from 0x8950 to 0xa294}void build_fun_asm(char *asm_bin, int *len){unsigned long block_uid = 10037;append_asm(asm_bin, len, 0xB502, 2); //push {r1,lr}//fun asmappend_asm(asm_bin, len, 0xB4FF, 2); // push {r0-r7}append_asm(asm_bin, len, 0x6967, 2); // LDR R7, [R4,#0x14]append_asm(asm_bin, len, 0x4B05, 2); // ldr r3,=block_uidappend_asm(asm_bin, len, 0x429F, 2); // CMP R7, R3append_asm(asm_bin, len, 0xBCFF, 2); // pop {r0-r7}append_asm(asm_bin, len, 0xD002, 2); // BEQ return -1//returnappend_asm(asm_bin, len, 0x6803, 2); //LDR R3, [R0]append_asm(asm_bin, len, 0x6822, 2); //LDR R2, [R4]append_asm(asm_bin, len, 0xBD02, 2); //pop {r1,pc}//return -1append_asm(asm_bin, len, 0x2300, 2); //mov r3, #0append_asm(asm_bin, len, 0x2201, 2); //mov r2, #1append_asm(asm_bin, len, 0xBD02, 2); //pop {r1,pc}// append_asm(asm_bin, len, 0x1C00, 2); //nopappend_asm(asm_bin, len, 0x0000, 2); //nop//write return addressappend_asm(asm_bin, len, block_uid, 4);}void print_asm(char *asm_bin, int len){int i;for(i = 0; i < len; i++){printf("%x ", *(asm_bin + i));}printf("\n\n");}void tracePro(int pid){unsigned long replace_addr;unsigned long freeaddr;char asm_jump[32];int asm_jump_len = 0;char asm_fun[1024];int asm_fun_len = 0;char temp[1024];replace_addr = 0x8950;freeaddr = 0xA294;build_jmp_asm(asm_jump, &asm_jump_len, freeaddr);build_fun_asm(asm_fun, &asm_fun_len);putdata(pid, replace_addr, asm_jump, asm_jump_len);putdata(pid, freeaddr, asm_fun, asm_fun_len);getdata(pid, replace_addr, temp, 64);print_asm(temp, 64);getdata(pid, freeaddr, temp, 64);print_asm(temp, 64);}int main(int argc, char *argv[]){if(argc != 2){printf("Usage: %s <pid to be traced>\n", argv[0]);return 1;}pid_t traced_process;int status;traced_process = atoi(argv[1]);if(0 != ptrace(PTRACE_ATTACH, traced_process, NULL, NULL)){printf("Trace process failed:%d.\n", errno);return 1;}tracePro(traced_process);ptrace(PTRACE_DETACH, traced_process, NULL, NULL);return 0;}
在 0x8950 嵌入 asm_jump 代码,在 0xA294 嵌入 asm_fun 代码。
程序跑到 0x8950,就会跳到 0xA294 执行 ShellCode,之后,再返回原函数。
http://yunpan.cn/cmaRCiUxC9FcU 访问密码 6414
编译
$ ndk-build[armeabi] Cygwin : Generating dependency file converter script[armeabi] Compile thumb : inject <= inject.cjni/../inject.c: In function 'append_asm':jni/../inject.c:16:19: warning: initialization from incompatible pointer type [enabled by default]char *aasmp = &aasm;^jni/../inject.c: In function 'getdata':jni/../inject.c:55:14: warning: assignment makes integer from pointer without a cast [enabled by default]str[len] = ""; //''^[armeabi] Executable : inject[armeabi] Install : inject => libs/armeabi/inject
安装
$ cd libs/armeabi/$ adb -s emulator-5554 push inject /data/local/tmp/314 KB/s (9512 bytes in 0.029s)$ adb -s emulator-5554 shell## cd /data/local/tmp# chmod 755 inject
先运行SMSDemo,系统自带的短信程序,向另外一个模拟器5556发送短信。
没问题之后,就注入servicemanager进程。
# pssystem 28 1 804 276 c01a94a4 afd0b6fc S /system/bin/servicemanager# ./inject 28./inject 281 f0 a0 fc 9a 42 6f d1 30 1c 7 af 0 f0 c f9 30 1c 39 1c 0 f0 1e f9 7 9a 4 90 1a 2a 7 d1 35 4b 4 99 34 22 e8 58 ff f7 5e ee 0 28 b d0 4 98 ff f7 ad fe 31 4c 2 1c 31 49 28 59 79 442 b5 ff b4 67 69 5 4b 9f 42 ff bc 2 d0 3 68 22 68 2 bd 0 23 1 22 2 bd 0 1c 35 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
SMSDemo再尝试发送短信,发现已经不能,直接崩溃了,之后,再也无法启动,因为它无法请求任何服务,包括界面服务。