UWSCのスレッドに割り込む
スレッドとCPUの関連でスレッドの割り込みを検討したので、実証コードを書いてみた。
スレッドとCPUの関連について - じゅんじゅんのきまぐれ
実証スクリプト
UWSCのスレッドは、1秒毎にログに時間を出力するだけ。
ところが、MSGBOXにOKすると、Win32APIのMessageBoxが出力され、その間スレッドが止まる。
OPTION EXPLICIT CALL Asm TEXTBLOCK _INTERRUPT VYvsgewYAwAAi0UIiUX4x0XM/////4N9+AB0EYtN+IM5AHQJi1X4g3oEAHUIi0XM 6dECAADHRdRrZXJux0XYZWwzMsdF3AAAAADHReAAAAAAx0XkAAAAAI1F1FCLTfiL Ef/SiUXEx0XURnJlZcdF2ExpYnLHRdxhcnkAjUXUUItNxFGLVfiLQgT/0IlFyMdF 1E9wZW7HRdhUaHJlx0XcYWQAAI1N1FGLVcRSi0X4i0gE/9GJRfTHRdRDbG9zx0XY ZUhhbsdF3GRsZQCNVdRSi0XEUItN+ItRBP/SiUX8x0XUU3VzcMdF2GVuZFTHRdxo cmVhx0XgZAAAAI1F1FCLTcRRi1X4i0IE/9CJRfDHRdRSZXN1x0XYbWVUaMdF3HJl YWTHReAAAAAAjU3UUYtVxFKLRfiLSAT/0YlF6MdF1EdldFTHRdhocmVhx0XcZENv bsdF4HRleHTHReQAAAAAjVXUUotFxFCLTfiLUQT/0olF7MdF1FNldFSNRdRQi03E UYtV+ItCBP/QiUXQg330AA+EWgEAAIN9/AAPhFABAACDffAAD4RGAQAAg33oAA+E PAEAAIN97AAPhDIBAACDfdAAD4QoAQAAi034i1EIUmoAahr/VfSJRcCDfcAAD4QN AQAAi0XAUP9V8IlFzIN9zP8PhPIAAADHhej8//8BAAEAjY3o/P//UYtVwFL/VeyF wA+EtQAAAIuFrP3//4PoBImFrP3//4uNrP3//4uVkP3//4kRi4Ws/f//g+gEiYWs /f//i42s/f//i5WU/f//iRGLhaz9//+D6ASJhaz9//+Ljaz9//+LlZj9//+JEYuF rP3//4PoBImFrP3//4uNrP3//4tV+IkRi4Ws/f//g+gEiYWs/f//i42s/f//i5Wg /f//iRGLRfiLSAyJjaD9//+Nlej8//9Si0XAUP9V0IXAdQfHRcz9////6wfHRcz+ ////i03AUf9V6IlFvIN9zAB8BotVvIlVzItFwFD/VfyDfcgAdA2DfcQAdAeLTcRR /1XIi0XMi+VdwgQA ENDTEXTBLOCK TEXTBLOCK _INS_FUNC VYvsg+wcx0Xwa2VybsdF9GVsMzLHRfgAAAAAjUXwUItNCIsR/9KJRejHRfBGcmVl x0X0TGlicsdF+GFyeQCNRfBQi03oUYtVCItCBP/QiUXsx0XwdXNlcsdF9DMyAACN TfBRi1UIiwL/0IlF/MdF8E1lc3PHRfRhZ2VCx0X4b3hBAI1N8FGLVfxSi0UIi0gE /9GJReSDfeQAdB3HRfBTdG9wx0X0AAAAAGoAjVXwUo1F8FBqAP9V5IN97AB0DotN /FH/VeyLVehS/1Xsi00Qi1UUi0UMi+VdwhAA ENDTEXTBLOCK DIM funcIns = Asm.Set(_INS_FUNC) DIM funcInt = Asm.Set(_INTERRUPT) DIM paraSize = 16, para = Asm.Alloc(paraSize), i = 0 PUBLIC _threadId = 0 THREAD ProcThread() // スレッド起動待ち WHILE _threadId = 0 SLEEP(0.1) WEND IFB funcIns > 0 AND funcInt > 0 AND para > 0 THEN i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hK32, "LoadLibraryA")) i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hK32, "GetProcAddress")) i = i + Asm.SetDword(para + i, _threadId) i = i + Asm.SetDword(para + i, funcIns) WHILE MSGBOX("「OK」するとMessageBox表示", BTN_OK OR BTN_ABORT) = BTN_OK Asm.Run(funcInt, para, i) WEND // スレッド終了待ち _threadId = -1 WHILE _threadId = -1 SLEEP(0.1) WEND // 解放 Asm.Free(para) Asm.Free(funcInt) Asm.Free(funcIns) ENDIF PROCEDURE ProcThread() DEF_DLL GetCurrentThreadId(): DWORD: kernel32 _threadId = GetCurrentThreadId() WHILE _threadId > 0 GETTIME() PRINT G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2 SLEEP(1) WEND _threadId = 0 // スレッド終了マーク FEND
Asmモジュールはこちらから。
UWSCにできないことはない? - じゅんじゅんのきまぐれ
ネイティブコードのBase64エンコード文字列に空白・改行を自由に入れる都合上、
AsmモジュールをCrypt API版に修正しています。
UWSCのProcThread関数には、時間を出力するループしかないことがわかると思います。
解説
_INTERRUPTのコードのポイントは、以下の通り。
- SuspendしてCONTEXT設定してResumeは前書いた通り
- 引数を利用しstdcallに従うため、引数右からとEipをEsp領域に積む
_INS_FUNCのコードはこんな感じ。(厳密には違う)
DWORD __stdcall Proc(DWORD oeax, DWORD oecx, DWORD oedx) { MessageBox(NULL, "Stop", "Stop", 0); _asm { mov ecx, oecx; mov edx, oedx; } return oeax; }
stdcallでは、eax,ecx,edxは自由に使えるため、元の値を引数として渡す。
関数リターン時にeaxは返り値になるため、元のeaxはリターンに。
ecx,edxはその前に戻してやる。
UWSCのMSGBOX関数にOKした時の流れは以下の通り。
- ProcThreadを一時停止する
- stdcallを模して、引数をProcThreadのスタックに積む。スレッドのeipも積む
- スレッドのeipを、_INS_FUNCの先頭ポインターにする
- ProcThreadを再開し、メインスレッドは再度MSGBOX表示になる
- 再開したProcThreadはeipが_INS_FUNCの先頭ポインターなので、_INS_FUNCを実行
- メッセージボックスを出し、OK待ちする
- OKが押されたら、ecx,edxを元に戻す
- 元のeaxをリターンすることで、eaxも元に戻す
- リターン先は元のeipなので、UWSCのProcThread関数が再開される
ProcThread関数はどこで一時停止されるかわかりません。
GETTIME中かもしれないし、PRINT中かもしれない。
はたまた、割り込みしてMessageBoxを表示している最中かもしれない。
割り込んだ場合は、厳密にそこに戻る必要があるのです。
何回割り込めるか、、、?
スタックを消費しきるまでで、割り込み関数は64Byteくらい消費するかな。
UWSCのスレッドスタックサイズがどれくらいか知らないけど、1MBなら16384回くらい呼べる。
いや、UWSCが使っている分があるからもっと少ないか。
でも、限界に挑戦する気にはならないね、、、。
初期ページサイズ4KB=64回はクリアしてくれました(100回ほど割り込んでみた)
備考
「これさー、すごいのはわかったけど、何に使うの?」
その質問は無意味です。
登山家に何故山に登るの?と聞くようなもの。