Часть 2 - пример исследования кода в отладчике VxWorks. Первая часть статьи
здесь.
Исследовать код чисто умозрительно, разбирая последовательность инструкций в дизассемблере - конечно, можно, но достаточно тяжело. Регулярно возникает вопрос - а что в данный момент лежит в этом регистре? Перейдет ли управление по этой условной инструкции или нет? И так далее. Всегда хочется посмотреть регистры и память на живом модеме в интересующих нас точках. Фирма huawei сделала нам необычайно щедрый подарок - встроенный в VxWorks шелл с отладчиком. В качестве примера разберем такую задачу. Одной из подсистем VxWorks является процедура расчета кода разблокировки (знаменитый алгоритм v201). Когда пользователь вводит команду at^cardlock, модем рассчитывает nlock-код от своего IMEI, и сравнивает с введенным в команде. Если совпали - модем разблокируется. Не совпали - выводится сообщение ERROR. Анализом и обработкой AT-команд занимается Linux-часть модема. После приема команды через специальную подсистему обмена сообщениями (ICC) в VxWorks передается специальный запрос. По этому запросу VxWorks вычисляет и проверяет nlock-код, и возвращает в Linux через ICC ответ - совпало/не совпало. Мы попробуем заставить модем вычислить и показать нам 201-код от своего IMEI.
Команда ^cardlock - одноразовая. После того, как модем разблокировался, команда работать перестает. Но, к счастью, nlock-код требуется еще одной команде - at^datalock. Она используется для снятия блокировки с ряда служебных команд (типа ^nvrd/nvwr), и ее можно вводить сколько угодно раз. Вот с этой командой мы и будем работать.
Итак, у нас уже есть готовая IDA-база с образом VxWorks. Быстрый поиск по таблице символов дает нам процедуру с именем DRVAGENT_RcvDrvAgentDeviceDatalockSet. Именно эта процедура вызывается после приема сообщения от команды at^datalock. Но так ли это? Давайте проверим. Заходим в шелл VxWorks (C-shell) и вводим такую команду:
[C]->b DRVAGENT_RcvDrvAgentDeviceDatalockSet
value = 0 = 0x0
[C]->
Система ответила кодом 0 - команда принята. В результате этой команды мы установили отладочную точку (breakpoint) на адрес интересующей нас процедуры. Теперь открываем терминал на порт AT-команд модема, и вводим такую команду:
Код из всех единиц я выбрал произвольно - здесь может быть любое 8-разрядное число. Заметьте, что после нажатия enter модем не ответит привычными ОК или ERROR. Причина этого в том, что в VxWorks сработал breakpoint, и linux-часть модема еще не получила ответа. Переключаемся в консоль VxWorks и видим следующее:
Break at 0x513f6bdc: DRVAGENT_RcvDrvAgentDeviceDatalockSet Task: 0x53e964b8 (I0_TAF_FID)
[C]->
Наша точка останова сработала. Теперь мы уверены, что при вводе команды datalock управление приходит именно на процедуру DRVAGENT_RcvDrvAgentDeviceDatalockSet. Далее следует ввести команду "с" - тогда VxWorks продолжит свою работу, а в АТ-терминале появится долгожданный ответ ERROR.
Теперь займемся анализм кода в IDA. Сразу бросается в глаза вызов процедуры с громким названием MMA_VerifyOperatorLockPwd. Открываем ее, разбираем.
RAM:51463648 ; R0 - код введенный пользователем
RAM:51463648 ; Attributes: bp-based frame
RAM:51463648
RAM:51463648 MMA_VerifyOperatorLockPwd ; CODE XREF: OM_Authorize+28p
RAM:51463648 ; DRVAGENT_RcvDrvAgentDeviceDatalockSet+48p ...
RAM:51463648
RAM:51463648 var_28 = -0x28
RAM:51463648 c_imei = -0x24
RAM:51463648 var_15 = -0x15
RAM:51463648
RAM:51463648 MOV R12, SP
RAM:5146364C STMFD SP!, {R4,R5,R11,R12,LR,PC}
RAM:51463650 SUB R11, R12, #4
RAM:51463654 SUB R4, R11, #-c_imei
RAM:51463658 SUB SP, SP, #0x14
RAM:5146365C MOV R5, R0 ; r0 - PWD
RAM:51463660 MOV R2, #15 ; len
RAM:51463664 LDR R1, =g_aucMmaImei ; from
RAM:51463668 MOV R3, #0x111
RAM:5146366C MOV R0, R4 ; to
RAM:51463670 MOV R12, #0x3450
RAM:51463674 STR R12, [SP,#0x28+var_28]
RAM:51463678 BL V_MemCpy
RAM:5146367C MOV R0, R5 ; PWD - введенный пользователем пароль
RAM:51463680 MOV R1, R4 ; imeii
RAM:51463684 MOV R3, #0
RAM:51463688 STRB R3, [R11,#var_15]
RAM:5146368C BL VerifySL
RAM:51463690 RSBS R0, R0, #1
RAM:51463694 MOVCC R0, #0
RAM:5146369C LDMFD SP, {R4,R5,R11,SP,PC}
Как видно, все, что она делает, это берет IMEI модема из ячейки g_aucMmaImei, копирует его во временную переменную, и вызывает процедуру VerifySL. Смотрим эту процедуру.
Из анализа кода следует, что на ее вход передаются 2 параметра. В R0 лежит адрес строки, введенной нами в команде ^datalock. В R1 лежит адрес области памяти, хранящей IMEI модема. Давайте это проверим. Удаляем предыдущую точку останова, и ставим новую - на процедуре VerifySL.
[C]->bd
value = 0 = 0x0
[C]->b VerifySL
value = 0 = 0x0
Снова идем в АТ-терминал и вводим команду at^datalock. После срабатывания точки останова сморим содержимое регистров командой ti:
Break at 0x50d818c0: VerifySL Task: 0x53e964b8 (I0_TAF_FID)
[C]->ti
NAME ENTRY TID PRI STATUS PC SP ERRNO DELAY
---------- ------------ -------- --- ---------- -------- -------- ------- -----
I0_TAF_FID vos_FidTask 53e964b8 144 STOP 50d818c0 5414ef38 0 0
task stack: base 0x5414f000 end 0x54147000 size 32768 high 896 margin 31872
exc. stack: base 0x54151ffc end 0x54151000 start 0x54152000
exc. stack: size 4092 high 624 margin 3468
proc id: 0x5245028c ((null))
options: 0x9005
VX_SUPERVISOR_MODE VX_DEALLOC_STACK VX_DEALLOC_TCB VX_DEALLOC_EXC_STACK
VxWorks Events
--------------
Events Pended on : Not Pended
Received Events : 0x0
Options : N/A
r0 = 0x5372759c r1 = 0x5414ef3c r2 = 0x00000000
r3 = 0x00000000 r4 = 0x5414ef3c r5 = 0x5372759c
r6 = 0x53727580 r7 = 0x00000000 r8 = 0x5369fb60
r9 = 0x00000010 r10 = 0x0000000f r11/fp = 0x5414ef60
r12/ip = 0x32303634 r13/sp = 0x5414ef38 r14/lr = 0x51463690
pc = 0x50d818c0 cpsr = 0x600c0113 ttbase = 0x53f74000
value = 0 = 0x0
Теперь смотрим содержимое памяти по адресам из регистров R0 и R1:
[C]->d 0x5372759c,10,1
NOTE: memory values are displayed in hexadecimal.
0x53727590: 31 31 31 31 * 1111*
0x537275a0: 31 31 31 31 00 7f *1111............*
value = 0 = 0x0
[C]->d 0x5414ef3c,20,1
NOTE: memory values are displayed in hexadecimal.
0x5414ef30: 38 36 34 33 * 8643*
0x5414ef40: 34 36 30 32 36 39 39 38 33 31 35 00 80 75 72 53 *46026998315..urS*
Как видим, в первой области памяти лежат все 1 - то, что мы вводили в команде ^datalock. Во второй - IMEI нашего модема.
Анализируя процедуру VerifySL дальше, мы видим, что имеется 6 различных веток вычисленя кода, в зависимости от IMEI. Адреса процедур лежат в таблице unlock_func_table_v201:
ROM:51CED72C unlock_func_table_v201 DCD GetEncryptResult_201_1
RAM:51CED72C ; DATA XREF: VerifySL:loc_50D81988o
RAM:51CED72C ; ROM:off_50D819D8o
RAM:51CED730 DCD GetEncryptResult_201_2
RAM:51CED734 DCD GetEncryptResult_201_3
RAM:51CED738 DCD GetEncryptResult_201_4
RAM:51CED73C DCD GetEncryptResult_201_5
RAM:51CED740 DCD GetEncryptResult_201_6
RAM:51CED744 DCD GetEncryptResult_201_7
В конце концов происходит сравнение вычисленного nlock-кода и введенного пользователем:
RAM:50D81988 loc_50D81988 ; CODE XREF: VerifySL+74j
RAM:50D81988 LDR R3, =unlock_func_table_v201 ; таблица функций
RAM:50D8198C LDR R3, [R3,R1,LSL#2] ; выбираем адрес нужной функции
RAM:50D81990 CMP R3, #0 ; пустая функция ?
RAM:50D81994 BEQ fail_50D818EC ; да - ошибочный IMEI
RAM:50D81998 SUB R4, R11, #-var_28 ; R4=буфер под nlock-код
RAM:50D8199C MOV R0, R5 ; R0=imei
RAM:50D819A0 MOV R1, #0 ; R1=0
RAM:50D819A4 MOV R2, R4 ; R2=буфер под nlock
RAM:50D819A8 BLX R3 ; вызываем функцию
RAM:50D819AC CMP R0, #0 ; функция вернула ошибку
RAM:50D819B0 BEQ fail_50D818EC
RAM:50D819B4 MOV R0, R7 ; pwd
RAM:50D819B8 MOV R1, R4 ; nlock
RAM:50D819BC LDR R3, =strcmp
RAM:50D819C0 BLX R3 ; strcmp ; сравниваем
RAM:50D819C4 RSBS R0, R0, #1
RAM:50D819C8 MOVCC R0, #0 ; 0 - не совпало 1 - совпало
Сравнение выполняет функция strcmp. На ее вход подается вычисленный модемом код в виде строки, адрес которой лежит в регистре R1. Теперь мы можем поставить точку останова по адресу 50D819C0, и увидеть долгожданный nlock-код, продолжив выполнение командой c:
[C]->b 0x50D819C0
value = 0 = 0x0
[C]->c
Break at 0x50d819c0: VerifySL +0x100 Task: 0x53e964b8 (I0_TAF_FID)
[C]->ti
NAME ENTRY TID PRI STATUS PC SP ERRNO DELAY
---------- ------------ -------- --- ---------- -------- -------- ------- -----
I0_TAF_FID vos_FidTask 53e964b8 144 STOP 50d819c0 5414ef0c 0 0
task stack: base 0x5414f000 end 0x54147000 size 32768 high 896 margin 31872
exc. stack: base 0x54151ffc end 0x54151000 start 0x54152000
exc. stack: size 4092 high 624 margin 3468
proc id: 0x5245028c ((null))
options: 0x9005
VX_SUPERVISOR_MODE VX_DEALLOC_STACK VX_DEALLOC_TCB VX_DEALLOC_EXC_STACK
VxWorks Events
--------------
Events Pended on : Not Pended
Received Events : 0x0
Options : N/A
r0 = 0x537273ec r1 = 0x5414ef0c r2 = 0x00000006
r3 = 0x51bfe9c8 r4 = 0x5414ef0c r5 = 0x5414ef3c
r6 = 0x0000000f r7 = 0x537273ec r8 = 0x5369fb60
r9 = 0x00000010 r10 = 0x0000000f r11/fp = 0x5414ef34
r12/ip = 0x00000006 r13/sp = 0x5414ef0c r14/lr = 0x5414ef14
pc = 0x50d819c0 cpsr = 0x200c0113 ttbase = 0x53f74000
value = 0 = 0x0
[C]->d 0x5414ef0c,8,1
NOTE: memory values are displayed in hexadecimal.
0x5414ef00: 36 34 33 31 * 6341*
0x5414ef10: 35 30 38 39 *5084............*
value = 0 = 0x0
Вот так мы вычислили nlock-код c помощью модема. Этот код является абсолютно точным, образцовым. Можно при входе в процедуру VerifySL c помощью команды m вписать в память другой IMEI, и вычислить nlock-код от него. Я использовал эту возможность для отладки своего калькулятора кодов.
Возможности отладчика VxWorks очень обширны. Вот крайткий список полезных команд:
b - установить точку останова
bd - убрать точку останова
c - продолжить выполнение программы
s - сделать 1 шаг
so - сделать шаг без захода в подпрограммы
d - посмотреть память
l - дизассемблировать код (да, да - там и встроенный дизассемблер есть!)
ti - посмотреть регистры
m - изменить память
Предусмотрена там и справочная система - команда help и ее производные. Прилагаю к этому посту pdf с фирменным описанием шеллов VxWorks. Есть и другие доки от WindRiver, легко находящиеся на просторах интернета.
Надеюсь, моя статья подвигднет кого-нибдуь на изучение кода модема. Поверьте, это крайне увлекательное и полезное занятие!
vxworks_cli_tools_users_guide_6.2.pdf ( 642.59 КБ )
Сообщение отредактировал Abbat1985 - 09.07.20, 20:55Причина редактирования: В шапке