Разбор параметров командной строки
Наконец-то добрался до полезной практической задачи по корректному разбору параметров командной строки. На языках высокого уровня это делается чуть ли не одной командой, а на Ассемблере как обычно приходится все делать самостоятельно. Решение получилось универсальным, подходит как для консольных, так и для GUI-приложений. Для использования функции ParseCmdLine в сегменте данных надо предварительно определить следующую структуру:Code (Assembler) : Убрать нумерацию
- ; Структура для командной строки
- struct CMDLINE
- nCount dd ? ; Количество аргументов
- lpArgs dd ? ; Указатель на массив адресов строк
- lpArgStr dd ? ; Указатель на массив строк
- ends
Сама функция разбора командной строки у меня получилась такая. Для работы используется память из кучи (Heap), оригинальная командная строка в памяти не модифицируется.
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------------
- ; Функция разбора параметров командной строки (ANSI)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;
- ; Параметры:
- ; CmdStr - указатель на структуру CMDLINE
- ; dQuotFlag - сохранять кавычки: TRUE - сохранять, FALSE - убирать
- ; На выходе:
- ; заполненная структура CMDLINE с параметрами командной строки
- ;------------------------------------------------------------------
- proc ParseCmdLine CmdStr:dword, dQuotFlag:dword
- local _hheap:DWORD ; Локальные переменные
- local _tmp:DWORD
- local _nCount:DWORD
- local _dAct:DWORD
- virtual at 0
- localcmd CMDLINE ; Локальная проекция структуры
- end virtual
- pusha
- ; Получить адрес кучи
- invoke GetProcessHeap
- mov [_hheap],eax
- mov [_nCount],0 ; Обнулить счетчик аргументов
- mov [_dAct],0 ; Обнулить флаг "работаю"
- ; Выделить память для сохранения обработанных аргументов
- invoke HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,1000h
- mov edi,eax
- mov [_tmp],eax
- ; Получить командную строку
- invoke GetCommandLine
- mov esi,eax
- xor ebx,ebx ; Флажок-признак, что аргумент в кавычках
- .parse_scan:
- lodsb
- or al,al ; Строка закончилась?
- jz .parse_end
- cmp al,'"' ; Найдена кавычка в параметрах?
- jz .parse_quote
- cmp ebx,1 ; Уже обрабатывается строка в кавычках
- je .parse_arg_in_quote
- cmp al,' ' ; Встретили пробел, записать параметр
- je .parse_push_arg
- cmp al,09h ; Встретили табулятор, записать параметр
- je .parse_push_arg
- .parse_arg_in_quote:
- stosb ; Записать текущий символ
- mov [_dAct],1 ; Установить флаг "работаю"
- jmp .parse_scan
- .parse_push_arg:
- mov al,0 ; Окончание строки
- stosb
- inc [_nCount]
- mov [_dAct],0 ; Обнулить флаг "работаю"
- .parse_remove_space:
- lodsb ; Пропустить пробелы и табуляторы
- or al,al
- jz .parse_end
- cmp al,'"'
- je .parse_start_quote
- cmp al,' '
- je .parse_remove_space
- cmp al,09h
- je .parse_remove_space
- dec esi
- jmp .parse_scan
- ; Обработка кавычек в командной строке
- .parse_quote:
- or ebx,ebx
- jnz .parse_end_quote
- .parse_start_quote:
- ; Если флаг установлен, то записать и кавычки
- cmp [dQuotFlag],TRUE
- jne @f
- stosb
- @@:
- ; Установить флаг что обрабатывается параметр в кавычках
- mov ebx,1
- jmp .parse_scan
- .parse_end_quote:
- ; Если флаг установлен, то записать и кавычки
- cmp [dQuotFlag],TRUE
- jne @f
- stosb
- @@:
- xor ebx,ebx
- jmp .parse_push_arg
- .parse_end:
- ; Окончание параметров
- xor eax,eax
- stosw
- mov eax,[_dAct]
- add [_nCount],eax
- mov ecx,edi
- sub ecx,[_tmp]
- push ecx
- ; Выделить память
- invoke HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,ecx
- pop ecx
- mov edi,eax
- mov esi,[_tmp]
- mov eax,[CmdStr]
- mov [eax+localcmd.lpArgStr],edi ; lpArgStr
- ; Перенести строку параметров
- rep movsb
- ; Прибраться за собой
- invoke HeapFree,[_hheap],0,[_tmp]
- mov eax,[CmdStr]
- mov ebx,[_nCount]
- mov [eax+localcmd.nCount],ebx ; nCount
- ; Размер = _nCount * 4
- mov eax,[_nCount]
- shl eax,2
- ; Выделить память
- invoke HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,eax
- mov edi,eax
- mov eax,[CmdStr]
- mov [eax+localcmd.lpArgs],edi ; lpArgs
- ; Заполнить таблицу индексов
- mov esi,[eax+localcmd.lpArgStr]
- .parse_fill_addr:
- invoke lstrlen,esi
- or eax,eax
- jz .parse_exit
- mov [edi],esi
- add edi,4
- add esi,eax
- inc esi
- jmp .parse_fill_addr
- .parse_exit:
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- ;------------------------------------------------------------------
- ; Функция разбора параметров командной строки (Unicode)
- ; Copyright (C) ManHunter / PCL
- ; https://www.manhunter.ru
- ;
- ; Параметры:
- ; CmdStr - указатель на структуру CMDLINE
- ; dQuotFlag - сохранять кавычки: TRUE - сохранять, FALSE - убирать
- ; На выходе:
- ; заполненная структура CMDLINE с параметрами командной строки
- ;------------------------------------------------------------------
- proc ParseCmdLine CmdStr:dword, dQuotFlag:dword
- local _hheap:DWORD ; Локальные переменные
- local _tmp:DWORD
- local _nCount:DWORD
- local _dAct:DWORD
- pusha
- ; Получить адрес кучи
- invoke GetProcessHeap
- mov [_hheap],eax
- mov [_nCount],0 ; Обнулить счетчик аргументов
- mov [_dAct],0 ; Обнулить флаг "работаю"
- ; Выделить память для сохранения обработанных аргументов
- invoke HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,1000h
- mov edi,eax
- mov [_tmp],eax
- ; Получить командную строку
- invoke GetCommandLine
- mov esi,eax
- xor ebx,ebx ; Флажок-признак, что аргумент в кавычках
- .parse_scan:
- lodsw
- or al,al ; Строка закончилась?
- jz .parse_end
- cmp ax,'"' ; Найдена кавычка в параметрах?
- jz .parse_quote
- cmp ebx,1 ; Уже обрабатывается строка в кавычках
- je .parse_arg_in_quote
- cmp ax,' ' ; Встретили пробел, записать параметр
- je .parse_push_arg
- cmp ax,09h ; Встретили табулятор, записать параметр
- je .parse_push_arg
- .parse_arg_in_quote:
- stosw ; Записать текущий символ
- mov [_dAct],1 ; Установить флаг "работаю"
- jmp .parse_scan
- .parse_push_arg:
- mov al,0 ; Окончание строки
- stosw
- inc [_nCount]
- mov [_dAct],0 ; Обнулить флаг "работаю"
- .parse_remove_space:
- lodsw ; Пропустить пробелы и табуляторы
- or ax,ax
- jz .parse_end
- cmp ax,'"'
- je .parse_start_quote
- cmp ax,' '
- je .parse_remove_space
- cmp ax,09h
- je .parse_remove_space
- dec esi
- dec esi
- jmp .parse_scan
- ; Обработка кавычек в командной строке
- .parse_quote:
- or ebx,ebx
- jnz .parse_end_quote
- .parse_start_quote:
- ; Если флаг установлен, то записать и кавычки
- cmp [dQuotFlag],TRUE
- jne @f
- stosw
- @@:
- ; Установить флаг что обрабатывается параметр в кавычках
- mov ebx,1
- jmp .parse_scan
- .parse_end_quote:
- ; Если флаг установлен, то записать и кавычки
- cmp [dQuotFlag],TRUE
- jne @f
- stosw
- @@:
- xor ebx,ebx
- jmp .parse_push_arg
- .parse_end:
- ; Окончание параметров
- xor eax,eax
- stosd
- mov eax,[_dAct]
- add [_nCount],eax
- mov ecx,edi
- sub ecx,[_tmp]
- push ecx
- ; Выделить память
- invoke HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,ecx
- pop ecx
- mov edi,eax
- mov esi,[_tmp]
- mov eax,[CmdStr]
- mov [eax+CMDLINE.lpArgStr],edi ; lpArgStr
- ; Перенести строку параметров
- rep movsb
- ; Прибраться за собой
- invoke HeapFree,[_hheap],0,[_tmp]
- mov eax,[CmdStr]
- mov ebx,[_nCount]
- mov [eax+CMDLINE.nCount],ebx ; nCount
- ; Размер = _nCount * 4
- mov eax,[_nCount]
- shl eax,2
- ; Выделить память
- invoke HeapAlloc,[_hheap],HEAP_ZERO_MEMORY,eax
- mov edi,eax
- mov eax,[CmdStr]
- mov [eax+CMDLINE.lpArgs],edi ; lpArgs
- ; Заполнить таблицу индексов
- mov esi,[eax+CMDLINE.lpArgStr]
- .parse_fill_addr:
- invoke lstrlen,esi
- or eax,eax
- jz .parse_exit
- mov [edi],esi
- add edi,4
- shl eax,1
- add esi,eax
- inc esi
- inc esi
- jmp .parse_fill_addr
- .parse_exit:
- popa
- ret
- endp
Code (Assembler) : Убрать нумерацию
- section '.data' data readable writeable
- ...
- struct CMDLINE
- nCount dd ? ; Количество аргументов
- lpArgs dd ? ; Указатель на массив адресов строк
- lpArgStr dd ? ; Указатель на массив строк
- ends
- myarg CMDLINE ; Это наша командная строка
- ...
- section '.code' code readable executable
- ...
- stdcall ParseCmdLine,myarg,FALSE
- ; Если не заданы параметры в командной строке, то вывести
- ; информацию о ключах программы
- cmp [myarg.nCount],2
- jb show_usage
- ...
Code (Assembler) : Убрать нумерацию
- invoke GetCommandLine
- invoke CommandLineToArgvW,eax,dData
- or eax,eax
- jz loc_error
- ; EAX -> указатель на массив параметров
- push eax
- mov esi,eax
- ; Количество параметров
- mov ebx,[dData]
- @@:
- ; Указатель на следующий параметр
- lodsd
- ...
- ; EAX -> строка параметра
- ...
- ; Следующий параметр
- dec ebx
- or ebx,ebx
- jnz @b
- ; Прибраться за собой
- pop eax
- invoke LocalFree,eax
Просмотров: 11916 | Комментариев: 16
Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье
ManHunter
(20.02.2023 в 12:40):
Добавил юникодную версию ParseCmdLine, а также разбор параметров через CommandLineToArgvW. Архив обновлен.
Петренко
(12.04.2022 в 22:59):
Благодарствую за подсказку! Нужно опробовать :)
ManHunter
(12.04.2022 в 22:42):
Дописывание кода не самая тривиальная задача, да и подсунуть командную строку не так просто. Можно попробовать подменить вызов GetCommandLine на mov eax,fake_cmd и где-нибудь в файле впечатать эту фейковую командную строку. Но оптимальным решением будет патч проверки или лоадер.
Петренко
(12.04.2022 в 22:20):
Лоадер и bat-ничек - это, конечно, можно, но хотелось бы научиться и такому подходу. Я так понимаю, что он универсальный для всех программ без защиты. Как я это вижу, нужно в свободное место EXE-шника дописать некий код, который будет подсовывать проге заданные параметры, а затем поменять точку входа в PE-заголовке. Осталось понять, какой код туда дописать) Если есть такие примеры, буду благодарен за помощь. Или не примеры, а просто подсказка, по какому адресу/смещению что поменять.
ManHunter
(12.04.2022 в 21:49):
Если в программе есть проверка на наличие этого параметра, то конечно можно. Но смысл? Проще сделать лоадер или вообще обычный bat-ничек для запуска с нужными параметрами.
Петренко
(12.04.2022 в 21:06):
ManHunter, а можно как-нибудь убедить (пропатчить) программу, чтобы она считала, что её запустили с параметром? Например, запускаем XXX.EXE, а она думает, что её запустили как "XXX.EXE --help".
ManHunter
(25.10.2016 в 08:44):
*facepalm*
Завязывай лакать стекломой по утрам, от него, похоже, мозги протухают и глаза слепнут.
Завязывай лакать стекломой по утрам, от него, похоже, мозги протухают и глаза слепнут.
Дмитрий
(25.10.2016 в 08:31):
Привет!
Ф можно в Демо примере убрать антисирийский лозунг:
Kill Fuck Asad!
Ф можно в Демо примере убрать антисирийский лозунг:
Kill Fuck Asad!
SMaSm-94
(17.11.2014 в 10:37):
ManHunter, да уж, не думал, что моя процедура настолько неоптимизированная ... Но спасибо, в будущем постораюсь быть повнимательнее :)
ManHunter
(16.11.2014 в 02:11):
Эх, молодежь....
proc get_command_line_argument nArg:DWORD
mov eax,[nArg]
cmp eax,[myarg.nCount]
jb @f
xor eax,eax
ret
@@:
shl eax,2
add eax,[myarg.lpArgs]
mov eax,[eax]
ret
endp
proc get_command_line_argument nArg:DWORD
mov eax,[nArg]
cmp eax,[myarg.nCount]
jb @f
xor eax,eax
ret
@@:
shl eax,2
add eax,[myarg.lpArgs]
mov eax,[eax]
ret
endp
SMaSm-94
(12.11.2014 в 11:57):
Вот, на досуге написал процедурку для получения параметра командной строки по порядковому номеру.
; Как же без структуры))
cmdln CMDLINE
; Небольшой пример использования:
...
stdcall ParseCmdLine, cmdln, FALSE
stdcall get_command_line_argument, 1
or eax, eax
jz .null_argument
invoke MessageBox, HWND_DESKTOP, eax, NULL, MB_OK
...
; Сама процедура:
proc get_command_line_argument nArg:DWORD
xor eax, eax
push ebx
mov ebx, [cmdln.nCount]
dec ebx
cmp [nArg], ebx
ja @f
mov ebx, [nArg]
shl ebx, 2
push edi
xor edi, edi
push esi
mov esi, dword [cmdln.lpArgs]
add esi, ebx
lodsd
pop esi
pop edi
@@:
pop ebx
ret
endp
Возвращаемые значения:
Если EAX = 0, то параметра под данным номером не существует,
Иначе в EAX будет содержаться адрес строки в массиве lpArgStr.
; Как же без структуры))
cmdln CMDLINE
; Небольшой пример использования:
...
stdcall ParseCmdLine, cmdln, FALSE
stdcall get_command_line_argument, 1
or eax, eax
jz .null_argument
invoke MessageBox, HWND_DESKTOP, eax, NULL, MB_OK
...
; Сама процедура:
proc get_command_line_argument nArg:DWORD
xor eax, eax
push ebx
mov ebx, [cmdln.nCount]
dec ebx
cmp [nArg], ebx
ja @f
mov ebx, [nArg]
shl ebx, 2
push edi
xor edi, edi
push esi
mov esi, dword [cmdln.lpArgs]
add esi, ebx
lodsd
pop esi
pop edi
@@:
pop ebx
ret
endp
Возвращаемые значения:
Если EAX = 0, то параметра под данным номером не существует,
Иначе в EAX будет содержаться адрес строки в массиве lpArgStr.
chak_xakep
(04.04.2011 в 15:08):
ManHunter, ну тогда я спокоен. Спасибо ;)
ManHunter
(04.04.2011 в 15:01):
Конечно.
chak_xakep
(04.04.2011 в 15:01):
Да лан, не злись! :) Вопрос такой, освобождается ли память при завершении приложения?
ManHunter
(04.04.2011 в 14:06):
Повторяю: тебе лучше жевать, чем говорить. Если ты НЕ понимаешь смысл написанного, то зачем вообще что-то писать? Залезь под отладчиком, посмотри что в этой "утекшей" памяти находится, после этого можешь указывать мне на "ошибки".
chak_xakep
(04.04.2011 в 14:00):
Ахтунг! утечка памяти :) В 127 строке прибрались за собой (HeapFree), а в 138 строчке выделили и забыли освободить. Хотя не так уж и страшно зависит от кол-ва вызовов этой самой ParseCmdLine.
Добавить комментарий
Заполните форму для добавления комментария