[关闭]
@oro-oro 2015-09-17T09:59:31.000000Z 字数 10423 阅读 2592

3. HarryInject[1]

DroidHOOK


0. 准备

准备一个发短信的DEMO。

  1. package com.test.smsdemo;
  2. import android.os.Bundle;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.telephony.SmsManager;
  5. import android.view.View;
  6. import android.widget.Button;
  7. import android.widget.EditText;
  8. public class MainActivity extends AppCompatActivity {
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.activity_main);
  13. final EditText numText = (EditText) findViewById(R.id.num);
  14. final EditText contentText = (EditText) findViewById(R.id.content);
  15. Button button = (Button) findViewById(R.id.button);
  16. button.setOnClickListener(new View.OnClickListener() {
  17. @Override
  18. public void onClick(View v) {
  19. String num = numText.getText().toString();
  20. String content = contentText.getText().toString();
  21. SmsManager smsManager = SmsManager.getDefault();
  22. smsManager.sendTextMessage(num, null, content, null, null);
  23. }
  24. });
  25. }
  26. }
http://yunpan.cn/cmaY6L6iLtIDt  访问密码 ddf7

1. 目标

在 Android 2.3.3 平台下,不让SMSDemo发送短信。

发短信的操作并不是 APP 直接调用的,APP 会把发送请求发给 SmsService 服务进程,由这个服务进程再调用系统 API 来发短信。而 APP 要想获取 SmsService 服务,需要去 service_manager 查询这个服务的 ID,然后根据 ID 和相应的 service 利用 binder 进行通信。

考虑利用 ptrace 拦截 SMSDemo 请求系统 Service 的能力而不影响其他应用。

2. 分析注入点

http://androidxref.com/2.3.7/xref/frameworks/base/cmds/servicemanager/service_manager.c

  1. 189int svcmgr_handler(struct binder_state *bs,
  2. 190 struct binder_txn *txn,
  3. 191 struct binder_io *msg,
  4. 192 struct binder_io *reply)
  5. 193{
  6. 194 struct svcinfo *si;
  7. 195 uint16_t *s;
  8. 196 unsigned len;
  9. 197 void *ptr;
  10. 198 uint32_t strict_policy;
  11. 199
  12. 200// LOGI("target=%p code=%d pid=%d uid=%d\n",
  13. 201// txn->target, txn->code, txn->sender_pid, txn->sender_euid);
  14. 202
  15. 203 if (txn->target != svcmgr_handle) // <<<<<----- 这里是关键 -----<<<<<
  16. 204 return -1;
  17. 205
  18. 206 // Equivalent to Parcel::enforceInterface(), reading the RPC
  19. 207 // header with the strict mode policy mask and the interface name.
  20. 208 // Note that we ignore the strict_policy and don't propagate it
  21. 209 // further (since we do no outbound RPCs anyway).
  22. 210 strict_policy = bio_get_uint32(msg);
  23. 211 s = bio_get_string16(msg, &len);
  24. 212 if ((len != (sizeof(svcmgr_id) / 2)) ||
  25. 213 memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
  26. 214 fprintf(stderr,"invalid id %s\n", str8(s));
  27. 215 return -1;
  28. 216 }
  29. 217
  30. 218 switch(txn->code) {
  31. 219 case SVC_MGR_GET_SERVICE:
  32. 220 case SVC_MGR_CHECK_SERVICE:
  33. 221 s = bio_get_string16(msg, &len);
  34. 222 ptr = do_find_service(bs, s, len);
  35. 223 if (!ptr)
  36. 224 break;
  37. 225 bio_put_ref(reply, ptr);
  38. 226 return 0;
  39. 227
  40. 228 case SVC_MGR_ADD_SERVICE:
  41. 229 s = bio_get_string16(msg, &len);
  42. 230 ptr = bio_get_ref(msg);
  43. 231 if (do_add_service(bs, s, len, ptr, txn->sender_euid))
  44. 232 return -1;
  45. 233 break;
  46. 234
  47. 235 case SVC_MGR_LIST_SERVICES: {
  48. 236 unsigned n = bio_get_uint32(msg);
  49. 237
  50. 238 si = svclist;
  51. 239 while ((n-- > 0) && si)
  52. 240 si = si->next;
  53. 241 if (si) {
  54. 242 bio_put_string16(reply, si->name);
  55. 243 return 0;
  56. 244 }
  57. 245 return -1;
  58. 246 }
  59. 247 default:
  60. 248 LOGE("unknown code %d\n", txn->code);
  61. 249 return -1;
  62. 250 }
  63. 251
  64. 252 bio_put_uint32(reply, 0);
  65. 253 return 0;
  66. 254}
  67. 255

txn 是一个 binder_txn 结构体,通过txn->sender_euid可以获得用户 ID。
http://androidxref.com/2.3.7/xref/frameworks/base/cmds/servicemanager/binder.h

  1. 20struct binder_txn
  2. 21{
  3. 22 void *target; // 4个字节
  4. 23 void *cookie; // 4个字节
  5. 24 uint32_t code; // 4个字节
  6. 25 uint32_t flags; // 4个字节
  7. 26
  8. 27 uint32_t sender_pid; // 4个字节
  9. 28 uint32_t sender_euid;
  10. 29
  11. 30 uint32_t data_size;
  12. 31 uint32_t offs_size;
  13. 32 void *data;
  14. 33 void *offs;
  15. 34};
  16. 35

Android 中每个 APP 都被分配了一个用户,利用用户来进行权限管理,用户分配表在 /data/system/packages.list。

查看SMSDemo的用户ID

  1. # cat /data/system/packages.list
  2. ...
  3. com.test.smsdemo 10037 1 /data/data/com.test.smsdemo
  4. ...

观察215-216行,通过txn->sender_euid来判断是哪个 APP 在请求。这样就可以根据需要来屏蔽特定应用所有的服务请求了。

3. 方案

现在有两个选择:

  1. 写一个 tracer 程序,利用 ptrace attach 到 servicemanager 进程,在函数svcmgr_handler中添加断点。中断之后 tracer 根据txn->sender_euid的值来修改相应的寄存器或者内存值,达到改变程序流程的目的。这种做法需要 tracer 一直运行,处于调试 servicemanager 的状态,每次svcmgr_handler都需要从 tracer 过一次。如果 tracer 挂了会导致不可预料的结果。

  2. 写一个 tracer 程序,只运行一次,利用 ptrace attach 到 servicemanager 进程,在 text 段修改svcmgr_handler的逻辑并插入我们的代码,然后 dettach。这样的话 trace 不必长期运行,也不用每次调用都过 tracer,稳定性和效率都大大提高。

选择2。

4. 编写ShellCode

现在需要修改的代码段是218-219

  1. if (txn->target != svcmgr_handle)
  2. return -1;

修改为:

  1. if(txn->sender_euid == 10037) { // 刚才查到的 SMSDemo 的用户 ID
  2. txn->target = 0;
  3. svcmgr_handle = 1; // 这样下面的if代码块,必定返回-1.
  4. }
  5. if (txn->target != svcmgr_handle)
  6. return -1;

接下来就写ShellCode了。
adb pull /system/bin/servicemanager 将servicemanager导出,拉进 IDA,通过字符串invalid id %s\n定位到svcmgr_handler函数。

  1. .text:0000893C ; =============== S U B R O U T I N E =======================================
  2. .text:0000893C
  3. .text:0000893C
  4. .text:0000893C sub_893C ; DATA XREF: sub_8780+3Eo
  5. .text:0000893C ; .got:off_A15Co
  6. .text:0000893C
  7. .text:0000893C var_38 = -0x38
  8. .text:0000893C var_2C = -0x2C
  9. .text:0000893C s2 = -0x28
  10. .text:0000893C var_24 = -0x24
  11. .text:0000893C var_1C = -0x1C
  12. .text:0000893C
  13. .text:0000893C PUSH {R4-R7,LR}
  14. .text:0000893E LDR R5, =(_GLOBAL_OFFSET_TABLE_ - 0x894A)
  15. .text:00008940 MOVS R4, R1 /* R4 = R1 = *txn */
  16. .text:00008942 LDR R1, =(off_A158 - 0xA170)
  17. .text:00008944 SUB SP, SP, #0x24
  18. .text:00008946 ADD R5, PC ; _GLOBAL_OFFSET_TABLE_
  19. .text:00008948 STR R0, [SP,#0x38+var_24]
  20. .text:0000894A LDR R0, [R5,R1] ; unk_A1F0
  21. .text:0000894C STR R3, [SP,#0x38+var_2C]
  22. .text:0000894E MOVS R6, R2
  23. .text:00008950 LDR R3, [R0] /* 注入地址 */
  24. .text:00008952 LDR R2, [R4]
  25. .text:00008954 CMP R2, R3
  26. .text:00008956 BNE loc_8A38 /* txn->target != svcmgr_handle */
  27. .text:00008958 MOVS R0, R6
  28. .text:0000895A ADD R7, SP, #0x38+var_1C
  29. .text:0000895C BL sub_8B78
  30. .text:00008960 MOVS R0, R6
  31. .text:00008962 MOVS R1, R7
  32. .text:00008964 BL sub_8BA4
  33. .text:00008968 LDR R2, [SP,#0x38+var_1C]
  34. .text:0000896A STR R0, [SP,#0x38+s2]
  35. .text:0000896C CMP R2, #0x1A
  36. .text:0000896E BNE loc_8980
  37. .text:00008970 LDR R3, =(__data_start_ptr - 0xA170)
  38. .text:00008972 LDR R1, [SP,#0x38+s2] ; s2
  39. .text:00008974 MOVS R2, #0x34 ; n
  40. .text:00008976 LDR R0, [R5,R3] ; __data_start_ptr ; "android.os.IServiceManager"
  41. .text:00008978 BLX memcmp
  42. .text:0000897C CMP R0, #0
  43. .text:0000897E BEQ loc_8998
  44. .text:00008980
  45. .text:00008980 loc_8980 ; CODE XREF: sub_893C+32j
  46. .text:00008980 LDR R0, [SP,#0x38+s2]
  47. .text:00008982 BL sub_86E0
  48. .text:00008986 LDR R4, =(__sF_ptr - 0xA170)
  49. .text:00008988 MOVS R2, R0
  50. .text:0000898A LDR R1, =(aInvalidIdS - 0x8992)
  51. .text:0000898C LDR R0, [R5,R4] ; __sF
  52. .text:0000898E ADD R1, PC ; "invalid id %s\n"
  53. .text:00008990 ADDS R0, #0xA8 ; stream
  54. .text:00008992 BLX fprintf
  55. .text:00008996 B loc_8A38

我们需要在.text:00008950之前,获得*txn->sender_euid,判断是否为10037,如果是,则修改 R2 和 R3 的值让其直接返回。

Shell Code 如下:
asm.s

  1. push {r1,lr}
  2. push {r0-r7}
  3. LDR R7, [R4,#0x14] /* R4就是*txn,偏移20(0X14)个字节获得 euid */
  4. ldr r3,=10037
  5. CMP R7, R3
  6. pop {r0-r7}
  7. BEQ loc_ret_1
  8. LDR R3, [R0] /* 原来的指令 */
  9. LDR R2, [R4] /* 原来的指令 */
  10. pop {r1,pc}
  11. loc_ret_1:
  12. mov r3, #0
  13. mov r2, #1
  14. pop {r1,pc}

汇编:

  1. $ as -ahlm -mthumb asm.s
  2. ARM GAS asm.s page 1
  3. 1 0000 02B5 push {r1,lr}
  4. 2 0002 FFB4 push {r0-r7}
  5. 3 0004 6769 LDR R7, [R4,#0x14]
  6. 4 0006 054B ldr r3,=10037
  7. 5 0008 9F42 CMP R7, R3
  8. 6 000a FFBC pop {r0-r7}
  9. 7 000c 02D0 BEQ loc_ret_1
  10. 8 000e 0368 LDR R3, [R0]
  11. 9 0010 2268 LDR R2, [R4]
  12. 10 0012 02BD pop {r1,pc}
  13. 11 loc_ret_1:
  14. 12 0014 0023 mov r3, #0
  15. 13 0016 0122 mov r2, #1
  16. 14 0018 02BD0000 pop {r1,pc}
  17. 14 35270000

5. 编写Tracer程序

源码:

  1. #include <sys/ptrace.h>
  2. #include <sys/types.h>
  3. #include <sys/wait.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <stdlib.h>
  9. #include <sys/syscall.h>
  10. int long_size = sizeof(long);
  11. void append_asm(char *asm_bin, int *len, unsigned long aasm, int alen)
  12. {
  13. int i;
  14. char *aasmp = &aasm;
  15. for(i = 0; i < alen; i++)
  16. {
  17. *(asm_bin + (*len)) = *(aasmp + i);
  18. (*len)++;
  19. }
  20. }
  21. void getdata(pid_t pid, long addr,
  22. char *str, int len)
  23. {
  24. char *laddr;
  25. int i, j;
  26. union u
  27. {
  28. long val;
  29. char chars[long_size];
  30. } data;
  31. i = 0;
  32. j = len / long_size;
  33. laddr = str;
  34. while(i < j)
  35. {
  36. data.val = ptrace(PTRACE_PEEKDATA,
  37. pid, addr + i * 4,
  38. NULL);
  39. memcpy(laddr, data.chars, long_size);
  40. ++i;
  41. laddr += long_size;
  42. }
  43. j = len % long_size;
  44. if(j != 0)
  45. {
  46. data.val = ptrace(PTRACE_PEEKDATA,
  47. pid, addr + i * 4,
  48. NULL);
  49. memcpy(laddr, data.chars, j);
  50. }
  51. str[len] = '\0';
  52. }
  53. void putdata(pid_t pid, long addr, char *str, int len)
  54. {
  55. char *laddr;
  56. int i, j;
  57. union u
  58. {
  59. long val;
  60. char chars[long_size];
  61. } data;
  62. i = 0;
  63. j = len / long_size;
  64. laddr = str;
  65. while(i < j)
  66. {
  67. memcpy(data.chars, laddr, long_size);
  68. ptrace(PTRACE_POKEDATA, pid,
  69. addr + i * 4, data.val);
  70. ++i;
  71. laddr += long_size;
  72. }
  73. j = len % long_size;
  74. if(j != 0)
  75. {
  76. memcpy(data.chars, laddr, j);
  77. ptrace(PTRACE_POKEDATA, pid,
  78. addr + i * 4, data.val);
  79. }
  80. }
  81. void build_jmp_asm(char *asm_bin, int *len, unsigned long freeaddr)
  82. {
  83. append_asm(asm_bin, len, 0xFCA0F001, 4); //b from 0x8950 to 0xa294
  84. }
  85. void build_fun_asm(char *asm_bin, int *len)
  86. {
  87. unsigned long block_uid = 10037;
  88. append_asm(asm_bin, len, 0xB502, 2); //push {r1,lr}
  89. //fun asm
  90. append_asm(asm_bin, len, 0xB4FF, 2); // push {r0-r7}
  91. append_asm(asm_bin, len, 0x6967, 2); // LDR R7, [R4,#0x14]
  92. append_asm(asm_bin, len, 0x4B05, 2); // ldr r3,=block_uid
  93. append_asm(asm_bin, len, 0x429F, 2); // CMP R7, R3
  94. append_asm(asm_bin, len, 0xBCFF, 2); // pop {r0-r7}
  95. append_asm(asm_bin, len, 0xD002, 2); // BEQ return -1
  96. //return
  97. append_asm(asm_bin, len, 0x6803, 2); //LDR R3, [R0]
  98. append_asm(asm_bin, len, 0x6822, 2); //LDR R2, [R4]
  99. append_asm(asm_bin, len, 0xBD02, 2); //pop {r1,pc}
  100. //return -1
  101. append_asm(asm_bin, len, 0x2300, 2); //mov r3, #0
  102. append_asm(asm_bin, len, 0x2201, 2); //mov r2, #1
  103. append_asm(asm_bin, len, 0xBD02, 2); //pop {r1,pc}
  104. // append_asm(asm_bin, len, 0x1C00, 2); //nop
  105. append_asm(asm_bin, len, 0x0000, 2); //nop
  106. //write return address
  107. append_asm(asm_bin, len, block_uid, 4);
  108. }
  109. void print_asm(char *asm_bin, int len)
  110. {
  111. int i;
  112. for(i = 0; i < len; i++)
  113. {
  114. printf("%x ", *(asm_bin + i));
  115. }
  116. printf("\n\n");
  117. }
  118. void tracePro(int pid)
  119. {
  120. unsigned long replace_addr;
  121. unsigned long freeaddr;
  122. char asm_jump[32];
  123. int asm_jump_len = 0;
  124. char asm_fun[1024];
  125. int asm_fun_len = 0;
  126. char temp[1024];
  127. replace_addr = 0x8950;
  128. freeaddr = 0xA294;
  129. build_jmp_asm(asm_jump, &asm_jump_len, freeaddr);
  130. build_fun_asm(asm_fun, &asm_fun_len);
  131. putdata(pid, replace_addr, asm_jump, asm_jump_len);
  132. putdata(pid, freeaddr, asm_fun, asm_fun_len);
  133. getdata(pid, replace_addr, temp, 64);
  134. print_asm(temp, 64);
  135. getdata(pid, freeaddr, temp, 64);
  136. print_asm(temp, 64);
  137. }
  138. int main(int argc, char *argv[])
  139. {
  140. if(argc != 2)
  141. {
  142. printf("Usage: %s <pid to be traced>\n", argv[0]);
  143. return 1;
  144. }
  145. pid_t traced_process;
  146. int status;
  147. traced_process = atoi(argv[1]);
  148. if(0 != ptrace(PTRACE_ATTACH, traced_process, NULL, NULL))
  149. {
  150. printf("Trace process failed:%d.\n", errno);
  151. return 1;
  152. }
  153. tracePro(traced_process);
  154. ptrace(PTRACE_DETACH, traced_process, NULL, NULL);
  155. return 0;
  156. }

ptrace
在 0x8950 嵌入 asm_jump 代码,在 0xA294 嵌入 asm_fun 代码。
程序跑到 0x8950,就会跳到 0xA294 执行 ShellCode,之后,再返回原函数。

http://yunpan.cn/cmaRCiUxC9FcU  访问密码 6414

6. 编译测试

编译

  1. $ ndk-build
  2. [armeabi] Cygwin : Generating dependency file converter script
  3. [armeabi] Compile thumb : inject <= inject.c
  4. jni/../inject.c: In function 'append_asm':
  5. jni/../inject.c:16:19: warning: initialization from incompatible pointer type [enabled by default]
  6. char *aasmp = &aasm;
  7. ^
  8. jni/../inject.c: In function 'getdata':
  9. jni/../inject.c:55:14: warning: assignment makes integer from pointer without a cast [enabled by default]
  10. str[len] = ""; //''
  11. ^
  12. [armeabi] Executable : inject
  13. [armeabi] Install : inject => libs/armeabi/inject

安装

  1. $ cd libs/armeabi/
  2. $ adb -s emulator-5554 push inject /data/local/tmp/
  3. 314 KB/s (9512 bytes in 0.029s)
  4. $ adb -s emulator-5554 shell
  5. #
  6. # cd /data/local/tmp
  7. # chmod 755 inject

先运行SMSDemo,系统自带的短信程序,向另外一个模拟器5556发送短信。
没问题之后,就注入servicemanager进程。

  1. # ps
  2. system 28 1 804 276 c01a94a4 afd0b6fc S /system/bin/servicemanager
  3. # ./inject 28
  4. ./inject 28
  5. 1 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 44
  6. 2 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再尝试发送短信,发现已经不能,直接崩溃了,之后,再也无法启动,因为它无法请求任何服务,包括界面服务。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注