任意のプロセスのWM_SYSCOMMANDのSC_CLOSEを無視させる(その2)

前回は、x86オンリーでした。
今回は、「1ファイルでx64/x86両方に対応する」ことを目指しました。



その前に

任意のプロセスのWM_SYSCOMMANDのSC_CLOSEを無視させる方法自体は秘密です。
もちろん、掲載しているところに書いてありますが、それはアセンブリレベルになります。
アセンブリを読める人には隠蔽できてませんが、ま、それは良いものとします。
アセンブリ読めるならわかるでしょう)


どちらかと言うと、相手が何かによって、自身を呼びなおす部分が一番のポイントかと。

制約のクリア方法

任意のプロセスにちょっかいを出そうとすると、WOW64の壁が大きく立ちはだかります。
x86のDLLはx86でしか呼べない。
x64のDLLはx64でしか呼べない。
すなわちDLLフックするにしても、x86用とx64用を二つ用意するのが普通。


しかし、複数になるのは、嫌!


ここでふと思い出したのが、.netのAnyCPU。(結果的には関係ないですが、、、)
.netではプロセス間通信に、.net remotingが使えるのですが、
.net remoting部分をdllに追い出してAnyCPUにすると、x86/x64に関わらず通信できる!
.netならx86/x64の壁を越えられる!


、、、いやまて。
.netと言えばpowershell
powershellなら、相手にあわせて自分を呼びなおせばいいじゃない!
でも、メッセージフックのDLLは別ファイルだよね、、、。
いやまて、Asmモジュールがあれば!

ということでスクリプト

前置きが長くなりましたが、任意のプロセスのWM_SYSCOMMAND SC_CLOSEを無視させるpowershellスクリプトです。
Asmモジュール相当をC#のクラスとして実現しています。

$source = @"
using System;
using System.Runtime.InteropServices;

	public class Asm
	{
		public const string KERNEL32 = "kernel32";
		[DllImport(KERNEL32)]
		public extern static uint GetLastError();
		[DllImport(KERNEL32)]
		public static extern IntPtr GetModuleHandle(string lpModuleName);
		public static IntPtr hKernel32 = GetModuleHandle(KERNEL32);
		[DllImport(KERNEL32)]
		public extern static IntPtr LoadLibrary(string lpFileName);
		[DllImport(KERNEL32, CharSet = CharSet.Ansi, ExactSpelling = true)]
		public extern static IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
		[DllImport(KERNEL32)]
		public extern static bool FreeLibrary(IntPtr hModule);

		[DllImport(KERNEL32)]
		private extern static IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
		private const uint PROCESS_QUERY_INFORMATION = 0x0400;
		private const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
		[DllImport(KERNEL32)]
		public extern static bool CloseHandle(IntPtr hObject);
		[DllImport(KERNEL32)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool IsWow64Process([In] IntPtr hProcess, [Out] out bool lpSystemInfo);
		[DllImport(KERNEL32)]
		private extern static IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint fAllocType, uint flProtect);
		private const uint MEM_COMMIT = 0x1000;
		private const uint PAGE_READWRITE = 0x4;
		private const uint PAGE_EXECUTE_READWRITE = 0x40;
		[DllImport(KERNEL32)]
		private extern static bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, uint dwFreeType);
		private const uint MEM_RELEASE = 0x8000;
		[DllImport(KERNEL32)]
		private extern static IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
		[DllImport(KERNEL32)]
		public extern static uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
		public const uint WAIT_OBJECT_0 = 0;
		public const uint WAIT_TIMEOUT = 258;
		[DllImport(KERNEL32)]
		private extern static bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
		[DllImport(KERNEL32)]
		private extern static void RtlMoveMemory(IntPtr dest, IntPtr source, UIntPtr size);

		public static IntPtr Set(string code)
		{
			IntPtr ret = IntPtr.Zero;
			if (code != null)
			{
				byte[] data = Convert.FromBase64String(code);
				if (data.Length > 0)
				{
					ret = VirtualAlloc(IntPtr.Zero, (UIntPtr)data.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
					if (ret != IntPtr.Zero)
					{
						Set(ret, 0, data);
					}
				}
			}
			return ret;
		}
		public static IntPtr Alloc(int size)
		{
			return VirtualAlloc(IntPtr.Zero, (UIntPtr)size, MEM_COMMIT, PAGE_READWRITE);
		}
		public static bool Free(IntPtr addr)
		{
			return VirtualFree(addr, UIntPtr.Zero, MEM_RELEASE);
		}

		// 戻りはtypeによって異なる
		//  type=0   : スレッド終了コード
		//  type=1   : スレッド終了コード取得失敗エラーコード
		//  type=258 : スレッドハンドル
		//  type=他  : その他エラーコード
		public static IntPtr Run(IntPtr addr, IntPtr para, out uint type)
		{
			return Run(addr, para, out type, 0x7fffffff);
		}
		public static IntPtr Run(IntPtr addr, IntPtr para, out uint type, int timeout)
		{
			IntPtr ret = IntPtr.Zero;
			uint res = 0;
			IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, para, 0, out res);
			ret = (IntPtr)GetLastError();
			type = WAIT_TIMEOUT;
			if (timeout >= 0) type = WaitForSingleObject(hThread, (uint)timeout);
			switch (type)
			{
				case WAIT_OBJECT_0:
					if (GetExitCodeThread(hThread, out res))
					{
						ret = (IntPtr)res;
					}
					else
					{
						type = 1;
						ret = (IntPtr)GetLastError();
					}
					CloseHandle(hThread);
					break;
				case WAIT_TIMEOUT:
					ret = hThread;
					break;
			}
			return ret;
		}

		public static int Set(IntPtr p, int offset, byte[] data)
		{
			int ret = 0;
			if (data != null) ret = data.Length;
			if (ret > 0) Marshal.Copy(data, 0, (IntPtr)((Int64)p + offset), ret);
			return ret;
		}
		public static int Set(IntPtr p, int offset, string data, string conv)
		{
			return Set(p, offset, System.Text.Encoding.GetEncoding(conv).GetBytes(data));
		}
		public static int Set(IntPtr p, int offset, string data)
		{
			return Set(p, offset, System.Text.Encoding.Unicode.GetBytes(data));
		}
		public static int Set(IntPtr p, int offset, uint data)
		{
			return Set(p, offset, BitConverter.GetBytes(data));
		}
		public static int Set(IntPtr p, int offset, IntPtr data)
		{
			int ret = 0;
			if (Is64())
			{
				ret = Set(p, offset, BitConverter.GetBytes((Int64)data));
			}
			else
			{
				ret = Set(p, offset, BitConverter.GetBytes((Int32)data));
			}
			return ret;
		}

		public static byte[] Get(IntPtr p, int offset, int size)
		{
			byte[] ret = null;
			if (size >= 0)
			{
				ret = new byte[size];
				Marshal.Copy((IntPtr)((Int64)p + offset), ret, 0, size);
			}
			return ret;
		}
		public static uint GetUint32(IntPtr p, int offset)
		{
			return BitConverter.ToUInt32(Get(p, offset, sizeof(UInt32)), 0);
		}

		public static int IsWow64Hwnd(IntPtr hwnd)
		{
			int ret = -1;
			uint procId;
			if (GetWindowThreadProcessId(hwnd, out procId) > 0)
			{
				IntPtr hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, procId);
				if (hProc != IntPtr.Zero)
				{
					ret = 0;
					if (IsWow64(hProc)) ret = 1;
					CloseHandle(hProc);
				}
			}
			return ret;
		}
		public static bool IsWow64(IntPtr target)
		{
			bool ret = false;

			//IsWow64Processが使えるか調べる
			if (GetProcAddress(hKernel32, "IsWow64Process") != IntPtr.Zero)
			{
				//IsWow64Processを呼び出す
				if (!IsWow64Process(target, out ret)) ret = false;
			}

			return ret;
		}
		public static bool Is64()
		{
			return (IntPtr.Size == sizeof(Int64));
		}
		public static bool Is64OS()
		{
			bool ret = Is64();
			if (!ret) ret = IsWow64(System.Diagnostics.Process.GetCurrentProcess().Handle);
			return ret;
		}


		public static string Dump(IntPtr p, int size)
		{
			string ret = "", buf = "", bufa = "";
			byte[] data = new byte[size];
			Marshal.Copy(p, data, 0, size);
			for (int i = 0; i < size; i++)
			{
				buf += string.Format(" {0:X2}", data[i]);
				if (data[i] >= 0x20 && data[i] < 0x7f)
				{
					bufa += System.Text.Encoding.ASCII.GetChars(data, i, 1)[0];
				}
				else
				{
					bufa += ".";
				}
				if (i % 16 == 15)
				{
					ret += buf + " " + bufa;
					buf = "";
					bufa = "";
				}
			}
			if (buf.Length > 0) ret += buf.PadRight(49) + bufa;
			return ret;
		}



		private const string USER32 = "user32";
		[DllImport(USER32)]
		private extern static uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
		[DllImport(USER32)]
		private extern static bool GetCursorPos(out UInt64 lpPoint);
		[DllImport(USER32)]
		private extern static IntPtr WindowFromPoint(UInt64 point);
		[DllImport(USER32)]
		private extern static IntPtr GetParent(IntPtr hWnd);
		public static IntPtr GetFromPointWin(bool bWin)
		{
			IntPtr ret = IntPtr.Zero, parent = IntPtr.Zero;
			UInt64 point;
			if (GetCursorPos(out point))
			{
				ret = WindowFromPoint(point);
				while (bWin && ret != IntPtr.Zero)
				{
					parent = GetParent(ret);
					if (parent == IntPtr.Zero) break;
					ret = parent;
				}
			}
			return ret;
		}
	}
"@
Add-Type -Language CSharp -TypeDefinition $source
#([AppDomain]::CurrentDomain).GetAssemblies() | Out-Gridview

$target = [Asm]::GetFromPointWin($true)
$addr = $null
$offset = 1568
$paraSize = 40
$i = 0
if([Asm]::Is64OS()) {
	if([Asm]::Is64()) {
		if([Asm]::IsWow64Hwnd($target) -eq 1) {
			& (Join-Path ($PSHOME -replace 'system32','syswow64') 'powershell.exe') $MyInvocation.MyCommand.Path
			exit $?
		} else {
			#64実行
			$addr = [Asm]::Set(@"
				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
				RCQw/1BISIlEJDhIi0QkOEiDxFjD
"@);
			$offset = 2528
			$paraSize = 80
		}
	} else {
		if([Asm]::IsWow64Hwnd($target) -ne 1) {
			& (Join-Path ($PSHOME -replace 'syswow64','sysnative') 'powershell.exe') $MyInvocation.MyCommand.Path
			exit $?
		}
	}
}
if($addr -eq $null) {
	#32実行
	$addr = [Asm]::Set(@"
		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
		SCBRi1X8i0Ik/9CJRfiLRfiL5V3CEADM
"@);
}
$para = [Asm]::Alloc($paraSize)
if ($addr -ne 0 -and $para -ne 0 -and [Asm]::hKernel32 -ne 0) {
	$i += [Asm]::Set($para, $i, [Asm]::GetProcAddress([Asm]::hKernel32, "LoadLibraryA"))
	$i += [Asm]::Set($para, $i, [Asm]::GetProcAddress([Asm]::hKernel32, "GetProcAddress"))
	$i += [Asm]::Set($para, $i, [Asm]::GetProcAddress([Asm]::hKernel32, "FreeLibrary"))
	$i += [Asm]::Set($para, $i, $target -as [IntPtr])
	$i += [Asm]::Set($para, $i, (($addr -as [Int64]) + $offset) -as [IntPtr]);
	$res = [Asm]::Run($addr, $para, [ref]$offset)
	if($offset -eq [Asm]::WAIT_TIMEOUT) {
		#[Asm]::CloseHandle($res)
		$res = $offset
	}
	#[Asm]::Dump($para, $paraSize)
}
[void][Asm]::Free($para)
[void][Asm]::Free($addr)
$res


Asmモジュール相当のクラスをC#で作成しています。
Powershellから直接P/Invokeを呼ぶ方法もありますが、面倒なのでC#クラスにしました。
元のAsmモジュールでは、UWSCの機能があるので不要でしたが、以下のメソッドを追加。

  • IsWow64Hwnd : 引数のHWNDを持つプロセスが、WOW64上で動作しているか調べる
  • IsWow64 : 引数のプロセスハンドルのプロセスが、WOW64上で動作しているか調べる
  • Is64 : 自プロセスが、x64かx86かを調べる(IntPtrのサイズを見るという安直版)
  • Is64OS : OSがx64か調べる(自プロセスがx64ならx64だし、x86の場合もWOW64上ならx64ですね)
  • GetFromPointWin : 引数がtrueなら親ウインドウ、falseならマウスポインターが示すHWNDを取得


Is64OSの場合は、自分と対象プロセスが食い違う場合がある。
自分Is64/相手IsWow64の場合、powershellのパスをsystem32->syswow64にして自スクリプトを呼びなおす。
自分Is64/相手!IsWow64の場合、x64として実行。
自分!Is64/相手!IsWow64の場合、powershellのパスをsyswow64->sysnativeにして自スクリプトを呼びなおす。
上記以外は、x86として実行。
以下のようなところです。

if([Asm]::Is64OS()) {
	if([Asm]::Is64()) {
		if([Asm]::IsWow64Hwnd($target) -eq 1) {
			#自分64相手32
			& (Join-Path ($PSHOME -replace 'system32','syswow64') 'powershell.exe') $MyInvocation.MyCommand.Path
			exit $?
		} else {
			#64実行
			exit
		}
	} else {
		if([Asm]::IsWow64Hwnd($target) -ne 1) {
			#自分32相手64
			& (Join-Path ($PSHOME -replace 'syswow64','sysnative') 'powershell.exe') $MyInvocation.MyCommand.Path
			exit $?
		}
	}
}
#32実行


Powershellからネイティブコードを呼びだすサンプルでもありますが、、、ほぼメリットないですね。
.netは便利です。
関係ないけど、XP、はやく絶滅しないかなー(.net前提でコーディングしたいので)
なお、3.0と4.5に対応するのが良さそう(Vista/Win7/Win8のプリインストール都合)
Vistaが絶滅したら、3.5(Win7)と4.5(Win8)。


なお、.netのアセンブリは、SDKのCorFlags.exeがあれば、32bitと64bitを切り替えることができます。
が、別ファイルが必要なので今回は採用しませんでした。
(自分にマークして再実行できるか知らないですし)