人気ブログランキング | 話題のタグを見る

AutoHotKey_Lで、プロセス間通信経由して一般権限でRunしてみた。ver.1.2

 UIPIの制限を回避するため、常駐AHKは管理者権限で実行しているのだが、それゆえにRunした時に不要な特権が付与されてしまう。それを回避する手段はいくつかあるものの、どれも一長一短で、最終的に「一般権限の常駐AHKにプロセス間通信でパスを送ってRun」に行きついた。最初からそうしとけって話だが、旧人類なので常駐増やしたくないのだ!

 Receive2Run.ahkを一般権限で常駐させて、管理者権限AHKからRunNEXすれば、R2Rが代わりにRunしてくれる。

 "|"はEverythingにクエリ送るとき使ってたので、区切り文字はAHKらしく"`n"にした。

; 管理者権限のAHKに書くやつ

RunNEX(Cmdline, WorkDir = "", Opt = "", ByRef Pid = "") {
SetTitleMatchMode 2
DetectHiddenWindows On

sig := "RunNEX:"

SendHwnd := WinExist("Receive2Run. ahk_class AutoHotkey")
if (0 == SendHwnd) {
MsgBox, 262160, , Receive2Runが見つかりません。
return
}

if !WorkDir {
WorkDir := A_WorkingDir
}

StringToSend := sig Cmdline "`n" WorkDir "`n" Opt

Result := Send_WM_COPYDATA(StringToSend, "ahk_id " SendHwnd)
if (0 < Result) {
Pid := Result
} else {
MsgBox,Err!`n%Result%

}
}

;https://www.autohotkey.com/docs/commands/OnMessage.htm
Send_WM_COPYDATA(ByRef StringToSend, ByRef TargetScriptTitle) {
PtrSize := A_PtrSize ? A_PtrSize : 4
VarSetCapacity(CopyDataStruct, 3*PtrSize, 0)
SizeInBytes := (StrLen(StringToSend) + 1) * (A_IsUnicode ? 2 : 1)
NumPut(SizeInBytes, CopyDataStruct, PtrSize)
NumPut(&StringToSend, CopyDataStruct, 2*PtrSize)

SendMessage, 0x4a, 0, &CopyDataStruct,, %TargetScriptTitle%
return ErrorLevel
}
;/////////////////////////////////////////////////////////////////////////////////////
;
; Receive2Run.ahk
;
;/////////////////////////////////////////////////////////////////////////////////////
#SingleInstance, Force
#NoEnv
#Persistent
SetBatchLines, -1
Menu, Tray, Icon, SHELL32.dll, 262

sig := "RunNEX:"

if A_IsAdmin {
MsgBox, 262160, , 管理者権限で起動しないでください。
ExitApp
}

OnMessage(0x4a, "Receive_WM_COPYDATA")
return

;https://www.autohotkey.com/docs/commands/OnMessage.htm
Receive_WM_COPYDATA(wParam, lParam) {
global sig
PtrSize:=A_PtrSize ? A_PtrSize : 4
StringAddress := NumGet(lParam + 2*PtrSize)
CopyOfData := StrGet(StringAddress)

if RegExMatch(CopyOfData, "^" sig "([^\n]+)(?:\n([^\n]*))?(?:\n([^\n]*))?(?:\n([^\n]*))?$", $) {
TrgPath := $1
WorkDir := $2
Opt := $3

TrgPid := 0
Run, %TrgPath%, %WorkDir%, %Opt%, TrgPid
} else {
MsgBox, Err!`n%CopyOfData%

}
return TrgPid
}

 メッセージ受け取ってRunとかいかにも悪用されそうなので、とりあえず符丁つけてみた。使うときは双方のsigを適当に書き換えてね。
 送り主を確認するとか他にやるべきことがあるのでは?という気もするが、所詮「とりあえず」なんで……。

 他の手段、「一般ユーザーでRunAS」「CreateProcessWithTokenWでShellから一般権限をコピー」「Shellに引数をつけてRun」とかあるのだけど――。RunAsはパスワードを平文で記録したくないのでNG。CreateProcessWithTokenWは複雑で、フォーラムにあったのコピペで試してたけど、動作したりしなかったり。で、Shellに.lnkを実行させるハックを愛用してたのだけど、これも近頃調子悪くなってしまい、諦めて常駐増やしたっちゅー……。





オマケ

 R2RがなければShellに起動してもらう差分。フォルダウィンドウをプロセス分離する設定だと、ゾンビが大量発生してしまうっぽいので、それらしきものを強制終了する(それが嫌でR2Rを作った)。

  SendHwnd := WinExist("Receive2Run. ahk_class AutoHotkey")
if (0 == SendHwnd) { ; R2RがなければShellに起動してもらう
CheckTime := A_Now "." A_MSec "000+540"
Run, Explorer.exe "C:\Receive2Run.exe" ; R2Rのパスを指定
WinWait, Receive2Run., , 5

; R2R起動後に生成されたExplorerを強制終了
ExpCmd := "C:\WINDOWS\explorer.exe /factory,{75dff2b7-6936-4c06-a8bb-676a7b00b24b} -Embedding" ; 環境による?
StringReplace, ExpCmdEsc, ExpCmd, \, \\, All
queryEnum := ComObjGet("winmgmts:").ExecQuery("Select ProcessId from Win32_Process Where CommandLine='" ExpCmdEsc "' and CreationDate>='" CheckTime "'")._NewEnum()
if queryEnum[process] {
ExpPid := process.ProcessId
Process, Close, %ExpPid%
}

SendHwnd := WinExist("Receive2Run. ahk_class AutoHotkey")
if (0 == SendHwnd) {
MsgBox, 262160, , Receive2Runが見つかりません。
return
}
}

by LordNoesis | 2018-06-15 19:08 | テクノロジ | Trackback | Comments(2)
Commented by お世話になっている人 at 2024-03-24 13:29 x
githubでこちら見つけました。
結構便利です。

RunAs_general(exe:="", arg:="", workdir:="") {
ShellExecute(exe, arg, workdir)
}

ShellExecute(Parameters*) {
ComObjCreate("Shell.Application")
.Windows
.FindWindowSW(0, 0, 8, 0, 1)
.Document
.Application
.ShellExecute(Parameters*)
}
Commented by lordnoesis at 2024-03-25 14:25
情報ありがとう。なるほど、Comでシェルに実行させるのはシンプル。PIDが必要ない用途には十分ですね。雑なコマンドラインじゃ通らないのが玉に瑕。
https://gist.github.com/anonymous1184/fa660cf2312cc50b38621a7c9b23b628?permalink_comment_id=4579504#gistcomment-4579504
ブログトップ | ファンになる