В январе 2015 года на портале NetSPI была опубликована статья об извлечении памяти с IOS-устройств. Даже несмотря на то, что было представлено средство, упрощающее решение этой задачи, скрипт работал в iOS7 (и ниже) и требовал наличия отладчика GDB. Однако на данный момент не существует версии GDB, которая корректно работала бы в iOS8.
Автор: Steve Kerns
В январе 2015 года на портале NetSPI была опубликована статья об извлечении памяти с IOS-устройств. Даже несмотря на то, что было представлено средство, упрощающее решение этой задачи, скрипт работал в iOS7 (и ниже) и требовал наличия отладчика GDB. Однако на данный момент не существует версии GDB, которая корректно работала бы в iOS8.
Хотя есть несколько альтернатив GDB, извлечение памяти на устройствах, работающих под управлением iOS8+, не является столь простой задачей. Нам потребуются следующие приложения:
Естественно, нам необходим джейлбрейковый iPhone или iPad. В этой статье процедура джейлбрейка рассматриваться не будет.
Подключаемся к устройству через USB при помощи скрипта Tcprelay.py:
$ ./tcprelay.py -t 22:2222 1234:1234
Команда «tcprelay.py -t 22:2222 1234:1234» перенаправляет два локальных порта на устройство. Первый порт (2222) используется для подключения к устройству через SSH. Второй порт необходим для корректной работы debugserver.
Forwarding local port 2222 to remote port 22
Forwarding local port 1234 to remote port 1234
Incoming connection to 2222
Waiting for devices...
Connecting to device <MuxDevice: ID 17 ProdID 0x12a8 Serial '0ea150b00ba3deeacb42f399492b7990416a0c87' Location 0x14120000>
Connection established, relaying data
Incoming connection to 1234
Waiting for devices...
Connecting to device <MuxDevice: ID 17 ProdID 0x12a8 Serial '0ea150b00ba3deeacb42f399492b7990416a0c87' Location 0x14120000>
Connection established, relaying data
Далее нужно подключиться к iOS-устройству и запустить сервер отладки (предполагается, что вы уже скопировали на устройство все необходимые приложения). Если нет, можете воспользоваться scp.
$ ssh root@127.0.0.1 -p 2222
root@127.0.0.1's password:
Далее, если приложение уже запущено, выясняем имя при помощи команды ‘ps aux | grep <appname>’ и подсоединяемся к процессу через debugserver (используем имя, а не PID):
root# ./debugserver *:1234 -a appname
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-320.2.89
for arm64.
Attaching to process appname...
Listening to port 1234 for a connection from *...
Waiting for debugger instructions for process 0.
Команда ‘./debugserver *:1234 -a appname’ запускает приложение на порту 1234 и подключает отладочный сервер. Весь процесс займет некоторое время, так что будьте терпеливы.
На MAC’е запустите LLDB и подключитесь к отладочному серверу, запущенному на iOS-устройстве. Помните, что мы перенаправили порт 1234, на котором работает debugserver, на локальный порт 1234.
$ lldb
(lldb) process connect connect://127.0.0.1:1234
Process 2017 stopped
* thread #1: tid = 0x517f9, 0x380f54f0 libsystem_kernel.dylib mach_msg_trap + 20, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x380f54f0 libsystem_kernel.dylib mach_msg_trap + 20
libsystem_kernel.dylib mach_msg_trap:
-> 0x380f54f0 <+20>: pop {r4, r5, r6, r8}
0x380f54f4 <+24>: bx lr
libsystem_kernel.dylib mach_msg_overwrite_trap:
0x380f54f8 <+0>: mov r12, sp
0x380f54fc <+4>: push {r4, r5, r6, r8}
Теперь вы можете выгрузить информацию о секциях памяти этого приложения.
(lldb) image dump sections appname
Sections for '/private/var/mobile/Containers/Bundle/Application/F3CFF345-71FC-47C4-B1FB-
3DAC523C7627/appname.app/appname(0x0000000000047000)' (armv7):
SectID Type Load Address File Off. File Size Flags Section Name
---------- ---------------- --------------------------------------- ---------- ---------- ---------- ----------------------------
0x00000100 container [0x0000000000000000-0x0000000000004000)* 0x00000000 0x00000000 0x00000000 appname.__PAGEZERO
0x00000200 container [0x0000000000047000-0x00000000001af000) 0x00000000 0x00168000 0x00000000 appname.__TEXT
0x00000001 code [0x000000000004e6e8-0x000000000016d794) 0x000076e8 0x0011f0ac 0x80000400 appname.__TEXT.__text
0x00000002 code [0x000000000016d794-0x000000000016e5e0) 0x00126794 0x00000e4c 0x80000400 appname.__TEXT.__stub_helper
0x00000003 data-cstr [0x000000000016e5e0-0x0000000000189067) 0x001275e0 0x0001aa87 0x00000002 appname.__TEXT.__cstring
0x00000004 data-cstr [0x0000000000189067-0x00000000001a5017) 0x00142067 0x0001bfb0 0x00000002 appname.__TEXT.__objc_methname
0x00000005 data-cstr [0x00000000001a5017-0x00000000001a767a) 0x0015e017 0x00002663 0x00000002 appname.__TEXT.__objc_classname
0x00000006 data-cstr [0x00000000001a767a-0x00000000001abe0c) 0x0016067a 0x00004792 0x00000002 appname.__TEXT.__objc_methtype
0x00000007 regular [0x00000000001abe10-0x00000000001ac1b8) 0x00164e10 0x000003a8 0x00000000 appname.__TEXT.__const
0x00000008 regular [0x00000000001ac1b8-0x00000000001aeb20) 0x001651b8 0x00002968 0x00000000 appname.__TEXT.__gcc_except_tab
0x00000009 regular [0x00000000001aeb20-0x00000000001aeb46) 0x00167b20 0x00000026 0x00000000 appname.__TEXT.__ustring
0x0000000a code [0x00000000001aeb48-0x00000000001af000) 0x00167b48 0x000004b8 0x80000408 appname.__TEXT.__symbolstub1
0x00000300 container [0x00000000001af000-0x00000000001ef000) 0x00168000 0x00040000 0x00000000 appname.__DATA
0x0000000b data-ptrs [0x00000000001af000-0x00000000001af4b8) 0x00168000 0x000004b8 0x00000007 appname.__DATA.__lazy_symbol
0x0000000c data-ptrs [0x00000000001af4b8-0x00000000001af810) 0x001684b8 0x00000358 0x00000006 appname.__DATA.__nl_symbol_ptr
0x0000000d regular [0x00000000001af810-0x00000000001b2918) 0x00168810 0x00003108 0x00000000 appname.__DATA.__const
0x0000000e objc-cfstrings [0x00000000001b2918-0x00000000001ba8d8) 0x0016b918 0x00007fc0 0x00000000 appname.__DATA.__cfstring
0x0000000f data-ptrs [0x00000000001ba8d8-0x00000000001baf1c) 0x001738d8 0x00000644 0x10000000 appname.__DATA.__objc_classlist
0x00000010 regular [0x00000000001baf1c-0x00000000001baf4c) 0x00173f1c 0x00000030 0x10000000 appname.__DATA.__objc_nlclslist
0x00000011 regular [0x00000000001baf4c-0x00000000001bafa0) 0x00173f4c 0x00000054 0x10000000 appname.__DATA.__objc_catlist
0x00000012 regular [0x00000000001bafa0-0x00000000001bafa4) 0x00173fa0 0x00000004 0x10000000 appname.__DATA.__objc_nlcatlist
0x00000013 regular [0x00000000001bafa4-0x00000000001bb078) 0x00173fa4 0x000000d4 0x00000000 appname.__DATA.__objc_protolist
0x00000014 regular [0x00000000001bb078-0x00000000001bb080) 0x00174078 0x00000008 0x00000000 appname.__DATA.__objc_imageinfo
0x00000015 data-ptrs [0x00000000001bb080-0x00000000001e0d40) 0x00174080 0x00025cc0 0x00000000 appname.__DATA.__objc_const
0x00000016 data-cstr-ptr [0x00000000001e0d40-0x00000000001e4420) 0x00199d40 0x000036e0 0x10000005 appname.__DATA.__objc_selrefs
0x00000017 regular [0x00000000001e4420-0x00000000001e442c) 0x0019d420 0x0000000c 0x00000000 appname.__DATA.__objc_protorefs
0x00000018 data-ptrs [0x00000000001e442c-0x00000000001e4ab8) 0x0019d42c 0x0000068c 0x10000000 appname.__DATA.__objc_classrefs
0x00000019 data-ptrs [0x00000000001e4ab8-0x00000000001e4e48) 0x0019dab8 0x00000390 0x10000000 appname.__DATA.__objc_superrefs
0x0000001a regular [0x00000000001e4e48-0x00000000001e6184) 0x0019de48 0x0000133c 0x00000000 appname.__DATA.__objc_ivar
0x0000001b data-ptrs [0x00000000001e6184-0x00000000001ea02c) 0x0019f184 0x00003ea8 0x00000000 appname.__DATA.__objc_data
0x0000001c data [0x00000000001ea030-0x00000000001ed978) 0x001a3030 0x00003948 0x00000000 appname.__DATA.__data
0x0000001d zero-fill [0x00000000001ed980-0x00000000001edce0) 0x00000000 0x00000000 0x00000001 appname.__DATA.__bss
0x0000001e zero-fill [0x00000000001edce0-0x00000000001edce8) 0x00000000 0x00000000 0x00000001 appname.__DATA.__common
0x00000400 container [0x00000000001ef000-0x0000000000207000) 0x001a8000 0x00015bf0 0x00000000 appname.__LINKEDIT
Следующий шаг – конвертирование результатов в команды LLDB для выгрузки данных в этих секциях памяти. Вы можете пропустить секции с именем zero-fill или code. Например, следующий текст:
БсщвуЮ 0x00000003 data-cstr
[0x000000000016e5e0-0x0000000000189067) 0x001275e0 0x0001aa87 0x00000002 appname.__TEXT.__cstring
Превращается в команду:
Memory read --outfile ~/0x00000003data-cstr 0x000000000016e5e0 0x0000000000189067 –force
Команда выше сообщает LLDB о том, что нужно выгрузить память, начиная с адреса 0x000000000016e5e0 и заканчивая адресом 0x0000000000189067, после чего положить результаты в файл 0x00000003data-cstr.
(lldb) memory read --outfile ~/0x00000003data-cstr 0x000000000016e5e0 0x0000000000189067 –force
После того как все файлы созданы, можно поискать конфиденциальную информацию: номера кредитных карт, пароли и т. д. Файлы будут содержать примерно следующие данные:
0x0016e5e0: 3f 3d 26 2b 00 3a 2f 3d 2c 21 24 26 27 28 29 2a ?=&+.:/=,!$&'()*
0x0016e5f0: 2b 3b 5b 5d 40 23 3f 00 00 62 72 61 6e 64 4c 6f +;[]@#?..brandLo
0x0016e600: 67 6f 2e 70 6e 67 00 54 72 61 64 65 47 6f 74 68 go.png.TradeGoth
0x0016e610: 69 63 4c 54 2d 42 6f 6c 64 43 6f 6e 64 54 77 65 icLT-BoldCondTwe
0x0016e620: 6e 74 79 00 4c 6f 61 64 69 6e 67 2e 2e 2e 00 4c nty.Loading....L
0x0016e630: 6f 61 64 69 6e 67 00 76 31 32 40 3f 30 40 22 4e oading.v12@?0@"N
0x0016e640: 53 44 61 74 61 22 34 40 22 45 70 73 45 72 72 6f SData"4@"EpsErro
0x0016e650: 72 22 38 00 6c 6f 61 64 69 6e 67 50 61 67 65 54 r"8.loadingPageT
0x0016e660: 79 70 65 00 54 69 2c 4e 2c 56 5f 6c 6f 61 64 69 ype.Ti,N,V_loadi
0x0016e670: 6e 67 50 61 67 65 54 79 70 65 00 6f 76 65 72 76 ngPageType.overv
0x0016e680: 69 65 77 52 65 71 52 65 73 48 61 6e 64 6c 65 72 iewReqResHandler
0x0016e690: 00 54 40 22 45 70 73 4f 76 65 72 76 69 65 77 52 .T@"EpsOverviewR
0x0016e6a0: 65 71 52 65 73 48 61 6e 64 6c 65 72 22 2c 26 2c eqResHandler",&,
0x0016e6b0: 4e 2c 56 5f 6f 76 65 72 76 69 65 77 52 65 71 52 N,V_overviewReqR
0x0016e6c0: 65 73 48 61 6e 64 6c 65 72 00 41 50 49 43 61 6c esHandler.APICal
Пользуйтесь этими техниками себе во благо и с добрыми намерениями J.
Напоследок приведу цитату из статьи, упомянутой в самом начале:
Эта техника может использоваться для того, чтобы определить, удаляет ли приложение конфиденциальную информацию из памяти после того, как отработали экземпляры классов. Все приложения должны очищать участки памяти, с которой работают классы и методы, обрабатывающие конфиденциальные сведения. Иначе есть риск, что эти данные попадут к злоумышленникам.