Тема: AHK: Параметры запущенного приложения
Добрый!
Каким образом можно узнать с какими параметрами было запущено приложение?
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Добрый!
Каким образом можно узнать с какими параметрами было запущено приложение?
Реализация на AutoHotKey, но на английском, с которым совсем туго
ProcessName:="svchost.exe"
Query:="Select * from Win32_Process where Name='" . ProcessName . "'"
Gui, Add, ListView, x0 y0 w500 h500, Process Name|Command Line|Pid
For Process In ComObjGet("winmgmts:").ExecQuery(Query)
LV_Add("", process.Name, Process.CommandLine, process.ProcessId)
Gui, Show,, Process List
Обнови AHK.
Спасибо большое. Работает только под _L, в базовой версии нигде не работает.
в базовой версии нигде не работает
И не должно!
он же без перебора процессов:
Process, Exist
pid := ErrorLevel
ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process WHERE ProcessId = " . pid)._NewEnum.next(x)
msgbox % x.CommandLine ;%
Спасибо большое. Работает только под _L, в базовой версии нигде не работает.
базовая версия не поддерживает "com". В теме на которую сослался teadrinker я рассказал как действовать в ней, но этот вариант был отсеян из-за сложности.
О, этот вариант гораздо более рационален, благодарю
Александр_, а нельзя ли выудить заодно "current directory" процесса?
Если это "на поверхности", конечно.
Ну и совсем заодно — ещё и pid parent процесса. Эта задача решается тут, но, м.б., есть вариант попроще.
---------
И ещё вопрос. Различные диспетчеры (PE, PH, SE) сканируют процессы "редко", и короткоживущие процессы не оставляют следа в списках. Есть ли какой способ отследить событие запуска процесса, если он достаточно быстро завершается? Это не очень в тему, но если не существует ответа, то не имеет смысла заводить отдельную.
Александр_, а нельзя ли выудить заодно "current directory" процесса?
Так нельзя, вот список возвращаемых данных. Я бы получал её как и писал в той теме.
Эта задача решается тут, но, м.б., есть вариант попроще.
Не знаю такого.
И ещё вопрос. Различные диспетчеры (PE, PH, SE) сканируют процессы "редко", и короткоживущие процессы не оставляют следа в списках. Есть ли какой способ отследить событие запуска процесса, если он достаточно быстро завершается? Это не очень в тему, но если не существует ответа, то не имеет смысла заводить отдельную.
В режиме пользователя это обычно реализуется через перехват функции ZwCreateThread во всех процессах. На AHK это довольно неудобно делать . Если нужно только зарегистрировать сам факт создания процесса, то можно включить аудит процессов в политике аудита windows и читать лог.
Mih, судя по вопросам тебе нужно на "си" переходить .
mih, вопрос не о программном решении задачи — то сие позволяет Process Monitor.
alexii, спасибо, действительно ProcMon показывает такие, я не пользовался им и не знал этого.
Александр_, спасибо тоже за дружеские советы, —
но не до си мне,
не до си, да.
Вот штука, сообщающая PID родительского процесса и его имя (при условии, если он ещё существует на момент работы скрипта), взято отсюда:
f11::
WinGet, Current_PID, PID, A
Parent_PID := GetParentProcessID(Current_PID)
Parent_Name:=GetProcessName(Parent_PID) ; имя parent процесса (при условии, если он ещё существует на момент работы скрипта)
MsgBox Current_PID: %Current_PID% Parent_PID: %Parent_PID% (это - %Parent_Name%)
Return
GetParentProcessID(ProcessID)
{
Return GetProcessInformation(ProcessID, "UInt *", 4, 24) ; DWORD th32ParentProcessID
}
GetProcessName(ProcessID)
{
Return GetProcessInformation(ProcessID, "Str", 260, 36) ; TCHAR szExeFile[MAX_PATH]
}
GetProcessInformation(ProcessID, CallVariableType, VariableCapacity, DataOffset)
{
hSnapshot := DLLCall("CreateToolhelp32Snapshot", "UInt", 2, "UInt", 0) ; TH32CS_SNAPPROCESS = 2
if (hSnapshot >= 0)
{
VarSetCapacity(PE32, 304, 0) ; PROCESSENTRY32 structure -> http://msdn2.microsoft.com/ms684839.aspx
DllCall("ntdll.dll\RtlFillMemoryUlong", "UInt", &PE32, "UInt", 4, "UInt", 304) ; Set dwSize
VarSetCapacity(th32ProcessID, 4, 0)
if (DllCall("Process32First", "UInt", hSnapshot, "UInt", &PE32)) ; http://msdn2.microsoft.com/ms684834.aspx
Loop
{
DllCall("RtlMoveMemory", "UInt *", th32ProcessID, "UInt", &PE32 + 8, "UInt", 4) ; http://msdn2.microsoft.com/ms803004.aspx
if (ProcessID = th32ProcessID)
{
VarSetCapacity(th32DataEntry, VariableCapacity, 0)
DllCall("RtlMoveMemory", CallVariableType, th32DataEntry, "UInt", &PE32 + DataOffset, "UInt", VariableCapacity)
DllCall("CloseHandle", "UInt", hSnapshot) ; http://msdn2.microsoft.com/ms724211.aspx
Return th32DataEntry ; Process data found
}
if not DllCall("Process32Next", "UInt", hSnapshot, "UInt", &PE32) ; http://msdn2.microsoft.com/ms684836.aspx
Break
}
DllCall("CloseHandle", "UInt", hSnapshot)
}
Return ; Cannot find process
}
mih пишет:Александр_, а нельзя ли выудить заодно "current directory" процесса?
Я бы получал её как и писал в той теме.
Код для AHK_L, можно переписать и под оригинальный:
;константы
ProcessBasicInformation := 0
PROCESS_QUERY_INFORMATION := 0x0400
PROCESS_VM_READ := 0x0010
;функции
;открытие процесса
OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId)
{
return DllCall("OpenProcess", "uint", dwDesiredAccess, "int", bInheritHandle, "uint", dwProcessId, "uint")
}
;получение информации о процессе
ZwQueryInformationProcess(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength)
{
return DllCall("ntdll.dll\ZwQueryInformationProcess","uint", ProcessHandle, "uint", ProcessInformationClass, "uint", ProcessInformation, "uint", ProcessInformationLength, "uint", ReturnLength, "uint")
}
;чтение данных из процесса
ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead)
{
return DllCall("ReadProcessMemory", "uint", hProcess, "uint", lpBaseAddress, "uint", lpBuffer, "uint", nSize, "uint", lpNumberOfBytesRead)
}
Process Exist ;, calc.exe
msgbox % GetProcCurrentDirectory(ErrorLevel) ;%
return
;получает текущую директорию процесса по его PID'у
GetProcCurrentDirectory(pid)
{
global ProcessBasicInformation, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ
hProc := OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid)
if (hProc=0)
{
return Error
}
VarSetCapacity(ProcessInformation, 0x18) ; под PROCESS_BASIC_INFORMATION
VarSetCapacity(Buffer, 8)
VarSetCapacity(ReturnLength, 4) ; формальность
ZwQueryInformationProcess(hProc, ProcessBasicInformation, &ProcessInformation, 0x18, &ReturnLength)
;по смещению 4 располагается адрес PEB
ReadProcessMemory(hProc, NumGet(&ProcessInformation, 4)+16, &Buffer, 4, &ReturnLength)
;по смещению 16 в PEB находится адрес структуры RTL_USER_PROCESS_PARAMETERS
ReadProcessMemory(hProc, NumGet(&Buffer)+36, &Buffer, 8, &ReturnLength)
; слово по смещению 0 содержит длину строки в байтах
; двойное слово по смещению 4 содержит адрес строки
; строка НЕ заканчивается нулём
x:=NumGet(&Buffer, 0, "ushort")
VarSetCapacity(str, x) ; выделяем необходимое место
ReadProcessMemory(hProc, NumGet(&Buffer, 4), &str, x, &ReturnLength)
x:=StrGet(&str, x/2, "UTF-16")
VarSetCapacity(ProcessInformation, 0) ; освобождаем память
VarSetCapacity(str, 0)
VarSetCapacity(Buffer, 0)
VarSetCapacity(ReturnLength, 0)
return x
}
mih пишет:И ещё вопрос. Различные диспетчеры (PE, PH, SE) сканируют процессы "редко", и короткоживущие процессы не оставляют следа в списках. Есть ли какой способ отследить событие запуска процесса, если он достаточно быстро завершается? Это не очень в тему, но если не существует ответа, то не имеет смысла заводить отдельную.
В режиме пользователя это обычно реализуется через перехват функции ZwCreateThread во всех процессах. На AHK это довольно неудобно делать . Если нужно только зарегистрировать сам факт создания процесса, то можно включить аудит процессов в политике аудита windows и читать лог.
Совсем забыл, в WMI ведь тоже есть всякие события/уведомления. Пример, который выводит сообщение при запуске процесса:
WMI := ComObjGet("winmgmts:").ExecNotificationQuery("SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'")
loop
{
WMIEvent := WMI.NextEvent()
msgbox % WMIEvent.TargetInstance.ExecutablePath ;%
}
return
Я бы не рекомендовал этим пользоваться, лучше уж самому раз в секунду проверять .
Александр_, оба скрипта работают. Рабочую директорию получаю, спасибо.
Но второй — опять же, мониторит только длительноживущие процессы (скрипт с одной стокой "sleep 500" он не замечает, "sleep 1000" — нормально). Поэтому — мою задачу в итоге не решает.
А задача была такая: очень редко (не каждый день) возникает процесс (напр., Блокнот), который запускает неизвестный мне некий "быстроживущий" процесс-родитель (его можно с'имитировать ahk-скриптом с одной строчкой запуска Блокнота) — я хочу этот запускающий процесс "найти и обезвредить". Надо среагировать на возникновение Блокнота (например, твоим скриптом), и сообщить информацию о родительском процессе. PID этого процесса может сообщить скрипт из поста 15, а вот его имя (или ком. строку), если к тому времени процесс уже завершён — добыть не удается. Почему-то система может выдать pid процесса, запустившего Блокнот, но ничего более про него она уже не помнит, — поэтому этот pid бесполезен (я дополнил скрипт в посте 15, теперь он сообщает и имя parent процесса, но при условии, если он существует на момент работы скрипта).
Если таково свойство системы, то такой путь к успеху не приведёт, но можно по-другому: мониторить любой новый процесс и запоминать всё о нём, смотреть, возник ли далее Блокнот, и, — если возник, сообщать всё о предыдущем процессе. Тут и нужен хорошо реагирующий скрипт, но — твой скрипт не видит "быстрых" процессов, и поэтому вроде как бесполезен. А ProcMon (как она прошла мимо меня, удивляюсь, спасибо Alex'у2), похоже, засекает любые "быстрые" процессы, так что возможности какие-то существуют; а может, они что-то в обход системы делают.
?
Я бы не рекомендовал этим пользоваться, лучше уж самому раз в секунду проверять smile.
Отчего? Утечка памяти была только с объектом Refresher.
Но второй — опять же, мониторит только длительноживущие процессы (скрипт с одной стокой "sleep 500" он не замечает, "sleep 1000" — нормально). Поэтому — мою задачу в итоге не решает.
Ну в запросе ведь указано проверять раз в секунду, можно и дробное число указать, например пол секунды:
ExecNotificationQuery("SELECT * FROM __InstanceCreationEvent WITHIN 0.5 WHERE TargetInstance ISA 'Win32_Process'")
Кстати, он не только за созданием процессов следит.
А задача была такая: очень редко (не каждый день) возникает процесс (напр., Блокнот), который запускает неизвестный мне некий "быстроживущий" процесс-родитель (его можно с'имитировать ahk-скриптом с одной строчкой запуска Блокнота) — я хочу этот запускающий процесс "найти и обезвредить". Надо среагировать на возникновение Блокнота (например, твоим скриптом), и сообщить информацию о родительском процессе. PID этого процесса может сообщить скрипт из поста 15, а вот его имя (или ком. строку), если к тому времени процесс уже завершён — добыть не удается. Почему-то система может выдать pid процесса, запустившего Блокнот, но ничего более про него она уже не помнит, — поэтому этот pid бесполезен (я дополнил скрипт в посте 15, теперь он сообщает и имя parent процесса, но при условии, если он существует на момент работы скрипта).
Если таково свойство системы, то такой путь к успеху не приведёт, но можно по-другому: мониторить любой новый процесс и запоминать всё о нём, смотреть, возник ли далее Блокнот, и, — если возник, сообщать всё о предыдущем процессе. Тут и нужен хорошо реагирующий скрипт, но — твой скрипт не видит "быстрых" процессов, и поэтому вроде как бесполезен.
Я выше писал- включи аудит процессов, скрипт вообще не нужен.
А ProcMon (как она прошла мимо меня, удивляюсь, спасибо Alex'у2), похоже, засекает любые "быстрые" процессы, так что возможности какие-то существуют; а может, они что-то в обход системы делают.
Process Monitor- это утилита от Марка Руссиновича, у него много подобных программ, позволяющих следить за работой ОС Windows, все они рассмотрены в его книгах серии "Windows Internals". Process Monitor устанавливает в систему собственный драйвер, поэтому скриптом его действия повторить нельзя.
Отчего? Утечка памяти была только с объектом Refresher.
Может не сработать из-за загруженности процессора. Правда в скриптах другого пути нету.
Да, насчет дробного числа - не подумал, но wmiprvse.exe начинает грузить процессор. Аудит посмотрел, вроде работает, но не вижу ком. строки процесса, и вообще всё это в топорном стиле MS: "сложно — о простом". Procmon, кажется, лучше (про утилиты Руссиновича, конечно, знал; но почему-то отложилось: procmon=regmon+filemon). Без скрипта, конечно, обойтись можно; — кстати, за чем он ещё следит?
Может не сработать из-за загруженности процессора.
У меня такого не случалось.
Да, насчет дробного числа - не подумал, но wmiprvse.exe начинает грузить процессор.
Ага, именно поэтому лучше самому список процессов проверять.
за чем он ещё следит?
А хрен его знает. Мне про WMI ничего хорошего не говорили, поэтому я не интересовался подробностями его работы.
У меня такого не случалось.
Ну это ведь просто логика! WMI тупо проверяет список каждые N секунд, соответственно если WMI не получит процессорное время, то не сможет вовремя сделать проверку. Вообще тема с запуском процессов уже изъезжена вдоль и поперёк. В режиме пользователя есть только функция CreateProcessNotify, её нужно экспортировать из dll, а саму библиотеку прописать в какой-то параметр реестра. У неё два существенных минуса- библиотека не подгружается в уже работающие процессы и при желании можно создать процесс в обход этого механизма. Все остальные методы базируются на полулегальных перехватах/подменах.
WMI тупо проверяет список каждые N секунд,
Откуда такая информация?
Его логика + моя эмпирика. Процессор грузится, если поставить проверку почаще, с 1ms заметно, — и грузится не скриптом. Прости, что встрял.
Возвращаясь к WMI и информации о процессах .
WMI почти всю информацию берёт из PEB целевого процесса, таким образом подменив там информацию можно обманывать приложения, использующие эту технологию. А командная строка вообще нигде не сохраняется, кроме как в PEB(по крайней мере я не нашёл). Таким образом, затерев командную строку мы обманем все приложения, которые будут получать её после модификации. Пример кода подменяющего командную строку(проверял только на win7 x64, но должно везде работать):
msgbox % GetCommandLine()
ChangeCommandLine("бугага")
msgbox % GetCommandLine()
return
GetCommandLine()
{
ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process WHERE ProcessId = " . DllCall("GetCurrentProcessId", "uint"))._NewEnum.next(x)
return x.CommandLine
}
ChangeCommandLine(NewCommandLine)
{
VarSetCapacity(ProcessInformation, 6*a_PtrSize, 0)
VarSetCapacity(ReturnLength, 4, 0)
x:=DllCall("ntdll.dll\ZwQueryInformationProcess", "ptr", -1, "uint", 0, "ptr", &ProcessInformation, "uint", 6*a_PtrSize, "ptr", &ReturnLength, "int")
if(x<0)
{
msgbox ZwQueryInformationProcess error, return = %x%, GetLastError = %a_lasterror%
return
}
lpPEB := NumGet(&ProcessInformation, A_PtrSize, "ptr")
if(!lpPEB)
{
msgbox не получен адрес PEB
return
}
lpProcessParameters := NumGet(lpPEB|0, A_PtrSize*4, "ptr")
if(!lpProcessParameters)
{
msgbox не получен адрес RTL_USER_PROCESS_PARAMETERS
return
}
MaxLen:=NumGet(lpProcessParameters|0, 18+12*A_PtrSize, "ushort")
CommandLine := NumGet(lpProcessParameters|0, 24+12*A_PtrSize, "ptr")
if(!CommandLine)
{
msgbox не получен адрес командной строки
return
}
len := strlen(NewCommandLine)*2+2
if(len>MaxLen)
len := MaxLen
NumPut(len-2, 16+12*A_PtrSize, "ushort")
StrPut(NewCommandLine, CommandLine|0, len/2, "UTF-16")
}
После выполнения функции ChangeCommandLine ни одно приложение не сможет получить командную строку этого скрипта.
len всегда будет больше MaxLen, при изменении len := strlen(NewCommandLine)*2 и, в частности, len = 12 — командная строка не изменяется.
len всегда будет больше MaxLen, при изменении len := strlen(NewCommandLine)*2 и, в частности, len = 12 — командная строка не изменяется.
Не понял .
MaxLen- это количество байт, выделенное под строку. len- это количество байт нужное для записи строки, не считая нуля на конце(т.е. косяк, нужно было ещё 2 прибавить). Далее если len>MaxLen, то просто записать строку нельзя, потому что может произойти переполнение и нужно либо обрезать строку, либо выделить другой участок памяти и прописать его в lpProcessParameters.
Или ты про опечатку в шестой строке снизу?
Или ты про опечатку в шестой строке снизу?
Именно так
С изменением len := strlen(NewCommandLine)*2+2 работает как предполагалось.
С изменением len := strlen(NewCommandLine)*2+2 работает как предполагалось.
Спасибо, поправил пост.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться