Реверсинг программы с использованием API-функции lstrcmp. Часть 1

Тема в разделе "WASM.ARTICLES", создана пользователем Alex81524, 16 янв 2019.

  1. Alex81524

    Alex81524 New Member

    Публикаций:
    5
    Регистрация:
    12 фев 2008
    Сообщения:
    8
    Рассмотрим один из самых простых вариантов поиска пароля – пароль вводится в виде строки символов и при этом используется API-функция lstrcmp.
    Вводимый пароль должен быть сравнен с эталонным паролем, который хранится в программе, а по результату сравнения выведены соответствующие сообщения. Проще всего для сравнения строк использовать API-функцию lstrcmp.
    Для простоты рассмотрения механизма сравнения проанализируем в отладчике x64Dbg программу консольного ввода пароля, потому что при использовании диалоговой программы необходимо привести весь ее шаблон, что резко увеличит ее сложность.
    Недостатками программы являются:
    – отсутствие диалога с пользователем. Пользователю не предлагается приглашение о вводе пароля, а предполагается, что он заранее знает, что в консольном окне необходимо ввести пароль;
    – низкая эргономика из-за консольного ввода;
    – длина пароля не ограничивается по длине символов. В программе проверяются только первые пять символов, а остальные не анализируются.
    Достоинства: простота кода и анализа.
    Код (ASM):
    1. Программа 2.1. Проверка пароля при консольном чтении
    2. ; программа проверки пароля при консольном чтении
    3. include win64a.inc
    4. .data
    5. szPas db "12345",0
    6. szStr1 db "Вы ввели корректный пароль. Поздравляем!",0
    7. szStr2 db "Вы ввели неправильный пароль.",0
    8. ttl db "Элементарная консольная программа ввода пароля",0
    9. str0 dq ?
    10. buf dq ?   ;
    11. .code
    12. WinMain proc
    13. sub rsp,28h; cтек: 28h=32d+8; 8 - возврат
    14. mov rbp,rsp
    15. invoke GetStdHandle,STD_INPUT_HANDLE
    16. invoke ReadConsole,rax,addr buf,5,addr str0,0
    17. invoke lstrcmp,addr szPas,addr buf ; сравнение строк символов
    18. .if rax==0
    19. invoke MessageBox,0,addr szStr1,addr ttl,MB_ICONINFORMATION
    20. .else
    21. invoke MessageBox,0,addr szStr2,addr ttl,MB_ICONERROR
    22. .endif
    23. invoke RtlExitUserProcess,0 ;ExitProcess,0
    24. WinMain endp
    25. end
    В связи с тем, что в программе используется консольное окно, то для получения ехе-файла необходимо в среде masm64 использовать bat-файл с командными словами SUBSYSTEM:CONSOLE. Если использовать bat-файл с командными словами SUBSYSTEM:WINDOWS, то ехе-файл получен будет, но запускаться – нет.
    Рассмотрим основные части программы.
    В программе функция GetStdHandle извлекает дескриптор для стандартного устройства ввода данных ( в данном случае – консоли).
    Функция ReadConsole читает символьный ввод данных из консольного буфера ввода.
    Синтаксис функции ReadConsole:
    Код (C):
    1. BOOL ReadConsole(
    2. HANDLE hConsoleInput , // дескриптор буфера ввода консоли
    3. LPVOID lpBuffer,  // буфер данных
    4. DWORD nNumberOfCharsToRead,  // число символов для чтения
    5. LPDWORD lpNumberOfCharsRead , // число прочитанных символов
    6. LPVOID lpReserved  // зарезервировано
    7. );
    Параметры функции ReadConsole:
    – hConsoleInput – дескриптор консольного буфера ввода. Дескриптор должен иметь право доступа GENERIC_READ;
    – lpBuffer – указатель на буфер, который принимает прочитанные данные из консольного буфера ввода;
    – nNumberOfCharsToRead – число прочитанных символов;
    – lpNumberOfCharsRead – указатель на переменную, которая принимает число фактически прочитанных;
    – lpReserved – зарезервировано, должно быть ПУСТО (NULL).
    Возвращаемые значения функцией ReadConsole:
    – если функция завершается успешно, то величина возвращаемого значения – не ноль;
    – если функция завершается с ошибкой, то величина возвращаемого значения – ноль. Чтобы получать расширенные данные об ошибках необходимо вызвать функцию GetLastError.
    Функция lstrcmp сравнивает две строки символов.
    Синтаксис:
    Код (C):
    1. int lstrcmp( LPCTSTR lpString1, LPCTSTR lpString2 );
    Параметры:
    – lpString1 – указатель на первую строку с завершающим нулем, которая будет сравниваться.
    – lpString2 – указатель на вторую строку с завершающим нулем, которая будет сравниваться.
    Возвращаемые значения (для архитектуры х64):
    – если строка, указанная при помощи lpString1, меньше, чем строка, указанная при помощи lpString2, то возвращаемое значение – единица;
    – если строка, указанная при помощи lpString1, больше, чем строка, указанная при помощи lpString2, то возвращаемое значение – единица;
    – если строки одинаковы, то возвращаемое значение – нуль.
    Детально рассмотрим этапы поиска пароля.
    1. Запускаем отладчик x64Dbg и открываем исследуемый ехе-файл. Для этого открываем в отладчике x64Dbg ехе-файл анализируемой программы и нажимаем F9 для ее выполнения (рис. 2.1).

    upload_2019-1-16_17-7-15.png
    В связи с тем, что программа очень легкая, занимает небольшой объем памяти и полностью вместилась в окне отладчика, то при внимательном рассмотрении сразу же виден и сам пароль, который ищем. Эталонный пароль расположен в первом параметре функции lstrcmp сравнения строк (пароль 12345). Вторым параметром этой функции будет строка, которая заполняется функцией ReadConsole при вводе текущего пароля и сохраняется в буфере.
    2. Находим функцию вывода сообщения. Для этого последовательно нажимаем в отладчике x64Dbg на клавишу F8 чтобы выполнить каждую строчку кода. При этом необходимо обратить внимание на строку, при которой происходит активизация окна консоли. Если продолжать несколько раз нажимать на F8 (при неподвижном курсоре отладчика x64Dbg), то при переходе на окно отладчика программа переходит на завершение анализа и все предстоит сделать с начала.

    3. Находим место анализа паролей. Для этого сначала в консольном окне вводим пароль, например, число 123 и нажимаем на клавиатуре кнопку Enter. При этом в окне отладчика x64Dbg строка курсора сдвинется вниз на одну позицию (рис. 2.2).
    upload_2019-1-16_17-7-55.png
    В строке
    00007FF65EE4103D48:8D15 40200000lea rdx,qword ptr ds:[7FF65EE43084]00007FF65EE43084:"123\r\n"
    в поле комментариев видно то значение пароля, которое ввели (значение 123).
    Чтобы посмотреть значение ячейки памяти необходимо подвести курсор к строке с адресом ячейки, нажать правую клавишу мышки и выбрать Перейти к дампу / Константа.

    Функция lstrcmp сравнивает две строки символов и после своего завершения по строке кода
    00007FF77A411044call qword ptr ds:[<&lstrcmp>]
    возвращает в регистре rax определенное значение (в соответствии со справочной информацией). А т.к. вводимый пароль (123) меньше, чем эталонный (12345), то rax=1.
    Команда cmp rax,0 сравниваем единицу (rax=1) с нулем. А вот если бы пароли совпали, то функция бы вернула ноль и сравнивалось два нуля.
    После команды сравнения располагается команда ветвления «Перейти, если не ноль» с адресом перехода
    jne pas1-64.7FF77A41106E
    на вывод сообщения о несовпадении пароля. Это и есть команда анализа признаков выполнения предыдущей операции (предыдущей операцией была команда cmp).
    4. Изменение команды ветвления.
    В связи с тем, что в анализируемой программе следующая строчка кода относится к параметрам функции MessageBox, которая выводит сообщение о правильном пароле, то можно осуществить перенаправление двумя вариантами:
    – вставить команды nop;
    – вставить команду jmp c новым адресом 00007FF77A411050. Этот вариант изменения является более универсальным.
    Чтобы вставить команды nop необходимо подвести курсор к строке с командой jne pas1-64.7FF77A41106E, дважды нажать левую клавишу мышки, ставим отметку в окошке Заполнить командами NOP и ввести NOP (рис. 2.3).
    upload_2019-1-16_17-8-46.png

    А если выбран вариант с заменой команды jne на команду jmp, то надо помнить, что требуется ввести еще и новый адрес перехода. Для этого, подводим курсор на строку, адрес которой необходимо запомнить (адрес строки команды xor rcx,rcx первой функции MessageBox), нажимаем правую клавишу мышки и выбираем Копировать / Адрес (в рассматриваемом примере – это адрес 00007FF778E51050).
    После копирования адреса подводим курсор к команде jne, дважды нажимаем левую клавишу мышки, заменяем адрес и обязательно второй слева символ заменяем на букву «х» (чтобы не прописывать еще и имя файла) (рис. 2.4).

    upload_2019-1-16_17-9-8.png
    5. Внесение изменений.
    Внести изменения в отладчике x64Dbg можно двумя способами.
    Первый способ: выбираем Файл/ Исправить файл. В новом окне выбрать пункт Исправить файл. Затем – новое имя и расширение ехе.
    Второй способ: выбрать пиктограмму с названием Исправления, нажать на кнопку Исправить файл, а далее новое имя и расширение ехе.
    После внесенных изменений ехе-файл с новым именем на любой пароль реагирует как на лицензионный.
    Рассмотрим применение программы 2.1. Для этого в начале приведем пример определения суммы элементов, а затем – этот же пример, но с частью кода проверки пароля.

    Пример 2.1. Задан массив А из N = 4 элементов. Привести программу определения суммы элементов массива А, для которых биты 0 и 5 совпадают.
    Программа 2.2. Выполнение примера 2.1:
    Код (ASM):
    1. include win64a.inc
    2. .data
    3. buf db 10 dup(?),0  ; буфер для вывода сообщения
    4. ifmt db "Задано массив из 4-х элементов:",0dh,0ah,\
    5. "10   12   13   14",0dh,0ah,0ah,\
    6.   "Сумма элементов массива, для которых биты 0 и 5 совпадают: = %d",10,10 ,\
    7.    "Автор программы:     Рысованый А.Н., НТУ ХПИ",0
    8. titl1 db "Сумма элементов главной диагонали",0 ; название окна
    9. mas1 dw 10, 12, 13, 14            ; массив mas1 слов
    10. len equ ($ -mas1) / type mas1 ; вычисления количества чисел в mas1
    11. sum dw 0                                 ; ячейка для хранения результата
    12. .code
    13. WinMain proc
    14. sub rsp,28h; cтек: 28h=32d+8; 8 - возврат
    15. mov rbp,rsp
    16. mov rcx,len   ; счетчик байтов
    17. lea rsi, mas1 ; адрес массива mas1
    18. m1: mov ax, [rsi]; занесение элемента массива
    19. bt ax, 0     ; выбор нулевого бита
    20. setc bh        ; если cf = 1, то установка 1 в bh
    21. bt ax, 5     ; выбор пятого бита
    22. setc bl       ; если cf = 1, то установка 1 в bl
    23. cmp bh, bl    ; сравнения битов
    24. jne m2        ; если не равно, то перейти на m2
    25. add sum, ax   ; сложение выбранных элементов массива
    26. m2: add rsi,2 ; увеличение адреса mas1 для выборки нового числа
    27. dec rcx       ; уменьшение счетчика чисел в массиве mas1
    28. jnz m1        ; перейти на метку m1, если не ноль
    29. movzx r15, sum ; сохранение результата с расширением разрядности
    30. invoke wsprintf,ADDR buf,ADDR ifmt,r15; функция преобразования
    31. invoke MessageBox,0,ADDR buf,ADDR titl1,MB_ICONINFORMATION
    32.  
    33. invoke RtlExitUserProcess,0 ;ExitProcess,0
    34. WinMain endp
    35. end
    Результатом выполнения программы является сумма трех цифр: 10, 12, 14, для которых биты 0 и 5 совпадают.
    Результат выполнения программы 2.2 приведен на рис. 2.5.

    upload_2019-1-16_17-10-43.png

    Приведем программу, в которой объединены две программы: программа 2.1 – проверки пароля и программы 2.2 – работа с массивом (программа 2.3).

    Программа 2.3. Проверка пароля при консольном чтении и выполнения примера 2.1:
    Код (ASM):
    1. include win64a.inc
    2. .data
    3. szPas db "12345",0
    4. szStr1 db "Вы ввели корректный пароль. Поздравляем!",0
    5. szStr2 db "Вы ввели неправильный пароль.",0
    6. ttl db "Элементарная консольная программа ввода пароля",0
    7. str0 dq ?
    8. buf dq ?,0   ;
    9. .code
    10. WinMain proc
    11. sub rsp,28h; cтек: 28h=32d+8; 8 - возврат
    12. mov rbp,rsp
    13. invoke GetStdHandle,STD_INPUT_HANDLE
    14. invoke ReadConsole,rax,addr buf,5,addr str0,0
    15. invoke lstrcmp,addr szPas,addr buf; сравнение двух строк символов
    16. .if rax==0
    17. invoke MessageBox,0,addr szStr1,addr ttl,MB_ICONINFORMATION
    18. jmp m10
    19. .else
    20. invoke MessageBox,0,addr szStr2,addr ttl,MB_ICONERROR
    21. .endif
    22. jmp m100
    23.  
    24. .data
    25. buf2 db 10 dup(?),0  ; буфер для вывода сообщения
    26. ifmt db "Задано массив из 4-х элементов:",0dh,0ah,\
    27. "10   12   13   14",0dh,0ah,0ah,\
    28.   "Сумма элементов массива, для которых биты 0 и 5 совпадают: = %d",10,10 ,\
    29.    "Автор программы:     Рысованый А.Н., НТУ ХПИ",0
    30. titl1 db "Сумма элементов главной диагонали",0 ; название окна
    31. mas1 dw 10, 12, 13, 14; массив mas1 слов
    32. len1 equ ($ -mas1) /type mas1; вычисления количества слов в mas1
    33. sum dw 0; ячейка для хранения результата
    34. .code
    35. m10:
    36. mov rcx,len1   ; счетчик байтов
    37. lea rsi, mas1 ; адрес массива mas1
    38. m1: mov ax, [rsi]; занесение элемента массива
    39. bt ax, 0     ; выбор нулевого бита
    40. setc bh        ; если cf = 1, то установка 1 в bh
    41. bt ax, 5     ; выбор пятого бита
    42. setc bl       ; если cf = 1, то установка 1 в bl
    43. cmp bh, bl    ; сравнения битов
    44. jne m2        ; если не равно, то перейти на m2
    45. add sum, ax   ; сложение выбранных элементов массива
    46. m2: add rsi,2 ; увеличение адреса mas1 для выборки нового числа
    47. dec rcx       ; уменьшение счетчика чисел в массиве mas1
    48. jnz m1        ; перейти на метку m1, если не ноль
    49. movzx r15, sum ; сохранение результата с расширением разрядности
    50. invoke wsprintf,ADDR buf2,ADDR ifmt,r15; функция преобразования
    51. invoke MessageBox,0,ADDR buf2,ADDR titl1,MB_ICONINFORMATION
    52. m100:
    53. invoke RtlExitUserProcess,0 ;ExitProcess,0
    54. WinMain endp
    55. end
    В программе 2.3. для простоты соединения часть кода, отвечающая за проверку пароля располагается в начале программы. В реальном случае желательно части кода проверки размешать частями внутри основной программы (путем использование команд jmp). Такой не сложный прием может затруднить анализ кода.
     
    Последнее редактирование модератором: 16 янв 2019
    Fail нравится это.
  2. Fail

    Fail Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2012
    Сообщения:
    503
    Спасибо за статью:drinks::good3: