Рассмотрим один из самых простых вариантов поиска пароля – пароль вводится в виде строки символов и при этом используется API-функция lstrcmp.
Вводимый пароль должен быть сравнен с эталонным паролем, который хранится в программе, а по результату сравнения выведены соответствующие сообщения. Проще всего для сравнения строк использовать API-функцию lstrcmp.
Для простоты рассмотрения механизма сравнения проанализируем в отладчике x64Dbg программу консольного ввода пароля, потому что при использовании диалоговой программы необходимо привести весь ее шаблон, что резко увеличит ее сложность.
Недостатками программы являются:
Достоинства: простота кода и анализа.
- отсутствие диалога с пользователем. Пользователю не предлагается приглашение о вводе пароля, а предполагается, что он заранее знает, что в консольном окне необходимо ввести пароль;
- низкая эргономика из-за консольного ввода;
- длина пароля не ограничивается по длине символов. В программе проверяются только первые пять символов, а остальные не анализируются.
В связи с тем, что в программе используется консольное окно, то для получения ехе-файла необходимо в среде masm64 использовать bat-файл с командными словами SUBSYSTEM:CONSOLE. Если использовать bat-файл с командными словами SUBSYSTEM:WINDOWS, то ехе-файл получен будет, но запускаться – нет.Код (ASM):
Программа 2.1. Проверка пароля при консольном чтении ; программа проверки пароля при консольном чтении include win64a.inc .data szPas db "12345",0 szStr1 db "Вы ввели корректный пароль. Поздравляем!",0 szStr2 db "Вы ввели неправильный пароль.",0 ttl db "Элементарная консольная программа ввода пароля",0 str0 dq ? buf dq ? ; .code WinMain proc sub rsp,28h; cтек: 28h=32d+8; 8 - возврат mov rbp,rsp invoke GetStdHandle,STD_INPUT_HANDLE invoke ReadConsole,rax,addr buf,5,addr str0,0 invoke lstrcmp,addr szPas,addr buf ; сравнение строк символов .if rax==0 invoke MessageBox,0,addr szStr1,addr ttl,MB_ICONINFORMATION .else invoke MessageBox,0,addr szStr2,addr ttl,MB_ICONERROR .endif invoke RtlExitUserProcess,0 ;ExitProcess,0 WinMain endp end
Рассмотрим основные части программы.
В программе функция GetStdHandle извлекает дескриптор для стандартного устройства ввода данных ( в данном случае – консоли).
Функция ReadConsole читает символьный ввод данных из консольного буфера ввода.
Синтаксис функции ReadConsole:
Параметры функции ReadConsole:Код (C):
BOOL ReadConsole( HANDLE hConsoleInput , // дескриптор буфера ввода консоли LPVOID lpBuffer, // буфер данных DWORD nNumberOfCharsToRead, // число символов для чтения LPDWORD lpNumberOfCharsRead , // число прочитанных символов LPVOID lpReserved // зарезервировано );
Возвращаемые значения функцией ReadConsole:
- hConsoleInput – дескриптор консольного буфера ввода. Дескриптор должен иметь право доступа GENERIC_READ;
- lpBuffer – указатель на буфер, который принимает прочитанные данные из консольного буфера ввода;
- nNumberOfCharsToRead – число прочитанных символов;
- lpNumberOfCharsRead – указатель на переменную, которая принимает число фактически прочитанных;
- lpReserved – зарезервировано, должно быть ПУСТО (NULL).
Чтобы получать расширенные данные об ошибках необходимо вызвать функцию GetLastError.
- если функция завершается успешно, то величина возвращаемого значения – не ноль;
- если функция завершается с ошибкой, то величина возвращаемого значения – ноль.
Функция lstrcmp сравнивает две строки символов.
Синтаксис:
Параметры:Код (C):
int lstrcmp( LPCTSTR lpString1, LPCTSTR lpString2 );
Возвращаемые значения (для архитектуры х64):
- lpString1 – указатель на первую строку с завершающим нулем, которая будет сравниваться.
- lpString2 – указатель на вторую строку с завершающим нулем, которая будет сравниваться.
Детально рассмотрим этапы поиска пароля.
- если строка, указанная при помощи lpString1, меньше, чем строка, указанная при помощи lpString2, то возвращаемое значение – единица;
- если строка, указанная при помощи lpString1, больше, чем строка, указанная при помощи lpString2, то возвращаемое значение – единица;
- если строки одинаковы, то возвращаемое значение – нуль.
Внести изменения в отладчике x64Dbg можно двумя способами.
- Запускаем отладчик x64Dbg и открываем исследуемый ехе-файл. Для этого открываем в отладчике x64Dbg ехе-файл анализируемой программы и нажимаем F9 для ее выполнения (рис. 2.1).
В связи с тем, что программа очень легкая, занимает небольшой объем памяти и полностью вместилась в окне отладчика, то при внимательном рассмотрении сразу же виден и сам пароль, который ищем. Эталонный пароль расположен в первом параметре функции lstrcmp сравнения строк (пароль 12345). Вторым параметром этой функции будет строка, которая заполняется функцией ReadConsole при вводе текущего пароля и сохраняется в буфере.- Находим функцию вывода сообщения. Для этого последовательно нажимаем в отладчике x64Dbg на клавишу F8 чтобы выполнить каждую строчку кода. При этом необходимо обратить внимание на строку, при которой происходит активизация окна консоли. Если продолжать несколько раз нажимать на F8 (при неподвижном курсоре отладчика x64Dbg), то при переходе на окно отладчика программа переходит на завершение анализа и все предстоит сделать с начала.
- Находим место анализа паролей. Для этого сначала в консольном окне вводим пароль, например, число 123 и нажимаем на клавиатуре кнопку Enter. При этом в окне отладчика x64Dbg строка курсора сдвинется вниз на одну позицию (рис. 2.2).
В строке
в поле комментариев видно то значение пароля, которое ввели (значение 123).
00007FF65EE4103D 48:8D15 40200000 lea rdx,qword ptr ds:[7FF65EE43084] 00007FF65EE43084:"123\r\n"
Чтобы посмотреть значение ячейки памяти необходимо подвести курсор к строке с адресом ячейки, нажать правую клавишу мышки и выбрать Перейти к дампу / Константа.
Функция lstrcmp сравнивает две строки символов и после своего завершения по строке кода
возвращает в регистре rax определенное значение (в соответствии со справочной информацией). А т.к. вводимый пароль (123) меньше, чем эталонный (12345), то rax=1.
00007FF77A411044 call qword ptr ds:[<&lstrcmp>]
Команда cmp rax,0 сравниваем единицу (rax=1) с нулем. А вот если бы пароли совпали, то функция бы вернула ноль и сравнивалось два нуля.
После команды сравнения располагается команда ветвления «Перейти, если не ноль» с адресом перехода
на вывод сообщения о несовпадении пароля. Это и есть команда анализа признаков выполнения предыдущей операции (предыдущей операцией была команда cmp).
jne pas1-64.7FF77A41106E
- Изменение команды ветвления.
В связи с тем, что в анализируемой программе следующая строчка кода относится к параметрам функции MessageBox, которая выводит сообщение о правильном пароле, то можно осуществить перенаправление двумя вариантами:Чтобы вставить команды nop необходимо подвести курсор к строке с командой jne pas1-64.7FF77A41106E, дважды нажать левую клавишу мышки, ставим отметку в окошке Заполнить командами NOP и ввести NOP (рис. 2.3).
- вставить команды nop;
- вставить команду jmp c новым адресом 00007FF77A411050. Этот вариант изменения является более универсальным.
А если выбран вариант с заменой команды jne на команду jmp, то надо помнить, что требуется ввести еще и новый адрес перехода. Для этого, подводим курсор на строку, адрес которой необходимо запомнить (адрес строки команды xor rcx,rcx первой функции MessageBox), нажимаем правую клавишу мышки и выбираем Копировать / Адрес (в рассматриваемом примере – это адрес 00007FF778E51050).
После копирования адреса подводим курсор к команде jne, дважды нажимаем левую клавишу мышки, заменяем адрес и обязательно второй слева символ заменяем на букву «х» (чтобы не прописывать еще и имя файла) (рис. 2.4).
- Внесение изменений.
Первый способ: выбираем Файл/ Исправить файл. В новом окне выбрать пункт Исправить файл. Затем – новое имя и расширение ехе.
Второй способ: выбрать пиктограмму с названием Исправления, нажать на кнопку Исправить файл, а далее новое имя и расширение ехе.
После внесенных изменений ехе-файл с новым именем на любой пароль реагирует как на лицензионный.
Рассмотрим применение программы 2.1. Для этого в начале приведем пример определения суммы элементов, а затем – этот же пример, но с частью кода проверки пароля.
Пример 2.1. Задан массив А из N = 4 элементов. Привести программу определения суммы элементов массива А, для которых биты 0 и 5 совпадают.
Программа 2.2. Выполнение примера 2.1:
Результатом выполнения программы является сумма трех цифр: 10, 12, 14, для которых биты 0 и 5 совпадают.Код (ASM):
include win64a.inc .data buf db 10 dup(?),0 ; буфер для вывода сообщения ifmt db "Задано массив из 4-х элементов:",0dh,0ah,\ "10 12 13 14",0dh,0ah,0ah,\ "Сумма элементов массива, для которых биты 0 и 5 совпадают: = %d",10,10 ,\ "Автор программы: Рысованый А.Н., НТУ ХПИ",0 titl1 db "Сумма элементов главной диагонали",0 ; название окна mas1 dw 10, 12, 13, 14 ; массив mas1 слов len equ ($ -mas1) / type mas1 ; вычисления количества чисел в mas1 sum dw 0 ; ячейка для хранения результата .code WinMain proc sub rsp,28h; cтек: 28h=32d+8; 8 - возврат mov rbp,rsp mov rcx,len ; счетчик байтов lea rsi, mas1 ; адрес массива mas1 m1: mov ax, [rsi]; занесение элемента массива bt ax, 0 ; выбор нулевого бита setc bh ; если cf = 1, то установка 1 в bh bt ax, 5 ; выбор пятого бита setc bl ; если cf = 1, то установка 1 в bl cmp bh, bl ; сравнения битов jne m2 ; если не равно, то перейти на m2 add sum, ax ; сложение выбранных элементов массива m2: add rsi,2 ; увеличение адреса mas1 для выборки нового числа dec rcx ; уменьшение счетчика чисел в массиве mas1 jnz m1 ; перейти на метку m1, если не ноль movzx r15, sum ; сохранение результата с расширением разрядности invoke wsprintf,ADDR buf,ADDR ifmt,r15; функция преобразования invoke MessageBox,0,ADDR buf,ADDR titl1,MB_ICONINFORMATION invoke RtlExitUserProcess,0 ;ExitProcess,0 WinMain endp end
Результат выполнения программы 2.2 приведен на рис. 2.5.
Приведем программу, в которой объединены две программы: программа 2.1 – проверки пароля и программы 2.2 – работа с массивом (программа 2.3).
Программа 2.3. Проверка пароля при консольном чтении и выполнения примера 2.1:
В программе 2.3. для простоты соединения часть кода, отвечающая за проверку пароля располагается в начале программы. В реальном случае желательно части кода проверки размешать частями внутри основной программы (путем использование команд jmp). Такой не сложный прием может затруднить анализ кода.Код (ASM):
include win64a.inc .data szPas db "12345",0 szStr1 db "Вы ввели корректный пароль. Поздравляем!",0 szStr2 db "Вы ввели неправильный пароль.",0 ttl db "Элементарная консольная программа ввода пароля",0 str0 dq ? buf dq ?,0 ; .code WinMain proc sub rsp,28h; cтек: 28h=32d+8; 8 - возврат mov rbp,rsp invoke GetStdHandle,STD_INPUT_HANDLE invoke ReadConsole,rax,addr buf,5,addr str0,0 invoke lstrcmp,addr szPas,addr buf; сравнение двух строк символов .if rax==0 invoke MessageBox,0,addr szStr1,addr ttl,MB_ICONINFORMATION jmp m10 .else invoke MessageBox,0,addr szStr2,addr ttl,MB_ICONERROR .endif jmp m100 .data buf2 db 10 dup(?),0 ; буфер для вывода сообщения ifmt db "Задано массив из 4-х элементов:",0dh,0ah,\ "10 12 13 14",0dh,0ah,0ah,\ "Сумма элементов массива, для которых биты 0 и 5 совпадают: = %d",10,10 ,\ "Автор программы: Рысованый А.Н., НТУ ХПИ",0 titl1 db "Сумма элементов главной диагонали",0 ; название окна mas1 dw 10, 12, 13, 14; массив mas1 слов len1 equ ($ -mas1) /type mas1; вычисления количества слов в mas1 sum dw 0; ячейка для хранения результата .code m10: mov rcx,len1 ; счетчик байтов lea rsi, mas1 ; адрес массива mas1 m1: mov ax, [rsi]; занесение элемента массива bt ax, 0 ; выбор нулевого бита setc bh ; если cf = 1, то установка 1 в bh bt ax, 5 ; выбор пятого бита setc bl ; если cf = 1, то установка 1 в bl cmp bh, bl ; сравнения битов jne m2 ; если не равно, то перейти на m2 add sum, ax ; сложение выбранных элементов массива m2: add rsi,2 ; увеличение адреса mas1 для выборки нового числа dec rcx ; уменьшение счетчика чисел в массиве mas1 jnz m1 ; перейти на метку m1, если не ноль movzx r15, sum ; сохранение результата с расширением разрядности invoke wsprintf,ADDR buf2,ADDR ifmt,r15; функция преобразования invoke MessageBox,0,ADDR buf2,ADDR titl1,MB_ICONINFORMATION m100: invoke RtlExitUserProcess,0 ;ExitProcess,0 WinMain endp end
Реверсинг программы с использованием API-функции lstrcmp. Часть 1
Дата публикации 22 мар 2019
| Редактировалось 17 май 2019