#include "stdafx.h"
#include "KeystrokeNoise.h"
#include "IATHook.h"
#include <Windows.h>
#include <stdlib.h>     // srand, rand
#include <time.h>       // time
#include <stdio.h>		// sprintf_s
#include <deque>
#include <vector>
using namespace std;

// The ASCII table has no code for the shift key, but we have to track when 
// we inject this keystroke. Choosing values that should't (hopefully?)
// interfere with ASCII codes, these are what we use to track when we 
// inject shift key-up and key-down:
#define SHIFTKEY_DOWN 0x01
#define SHIFTKEY_UP 0x02

// This bitmask is used when checking GetKeyState() or GetAsyncKeyState():
#define SHIFTED 0x8000

// Shared queue of injected keystrokes:
std::deque<WORD> gInjectedKeyQueue;
CRITICAL_SECTION gQueueLock;

// Function pointer prototypes for the DispatchMessage hook:
typedef int (WINAPI *LPFNDISPATCHMESSAGEW)(const MSG*);
LPFNDISPATCHMESSAGEW pDmOrig;
int WINAPI DispatchMessageHookProc(const MSG *lpmsg);

// Function prototype for the keyboard focus watch hook and the
// global event object used to signal focus changes to the
// keystroke injector thread:
bool AddHookToKeyboardFocusChanges();
HANDLE ghFocusEvent;

// Function prototype for the CallWindowProc hook:
LRESULT WINAPI CallWindowProcHook(int nCode, WPARAM wParam, LPARAM lParam);

// Function prototype for the keystroke injection thread proc:
DWORD WINAPI KeyInjectorThreadProc(LPVOID lpParameter);

// Function protoype (for a hack) to undo the effect of a shift:
WORD UndoShiftEffect(const WORD wKey);


/* -------------- For testing -----------------
// Bizarrely, although DispatchMessage can be hooked fine, a hook on MessageBoxW 
// appeared to never get called.
typedef int (WINAPI *LPFNMESSAGEBOXW)(HWND,LPCWSTR,LPCWSTR,UINT); // MessageBoxW
LPFNMESSAGEBOXW pMbOrig;

// This is the hook procedure I would install:
int WINAPI TestMessageBox(HWND wHandle, LPCWSTR text, LPCWSTR title, UINT type)
{
	return pMbOrig(NULL, L"If you see this then you succeeded", L"I guess", MB_OK);
}

// Next I would call the API, and for some reason the original got called:
MessageBoxW(NULL, L"If you see this then you screwed up", L"I guess", MB_OK);

// Example of how to output useful debugging information:
*/
TCHAR gtszString[256];
//swprintf_s(gtszString, (size_t)256, L"Random key generated: %c\n", (char)wKeyStroke);
//OutputDebugString(gtszString);
// -------------------------------------------- */

///////////////////////////////////////////////////////////////////////////////
// This is the top-level routine. Call this to kick off KeystrokeNoise. It
// returns true if successful, false if initialization failed.
//
bool InitializeKeystrokeNoise()
{
	// Initialize the critical section (only needs to be done once).
    if(!InitializeCriticalSectionAndSpinCount(&gQueueLock, 0x00000400))
	{
		// Apparently in Vista and newer, this API will never return zero. So this
		// message is only for Windows XP and 2003.
		OutputDebugString(L"Failed to initialize the critical section object.\n");
		return false;
	}

	// Insert a keystroke filter via an IAT hook of DispatchMessageW():
	pDmOrig = (LPFNDISPATCHMESSAGEW)HookImportedFunction(GetModuleHandle(NULL),"user32.dll","DispatchMessageW",(int)DispatchMessageHookProc);
	if(pDmOrig != NULL)
	{
		// Hook nicely to detect keyboard input focus change events:
		if(AddHookToKeyboardFocusChanges())
		{
			// Initiate the keystroke injector thread:
			HANDLE hInjectorThread = 
				CreateThread(NULL, // handle inheritability attributes = default
							  0, // dwStackSize = use process default
							  KeyInjectorThreadProc,
							  NULL, // optional ptr to parameter
							  0, // dwCreationFlags = start now
							  NULL);  // (optional out) pointer to thread ID
			if(hInjectorThread != NULL)
				return true;
		}
	}

	return false;
}


///////////////////////////////////////////////////////////////////////////////
// This routine is installed to hook the default DispatchMessage(), and filter
// out the injected keystroke noise at a vantage point after all known malware
// would have picked up the noise-filled keystroke stream.
//
// We have to go to a bit of trouble to handle the shift key, because it 
// causes the injected keystrokes to capitalize and change from their initial
// values. Our own injected keys include the shift key, because it is required
// to generate a realistic mix of capital and lowercase letters.
// 
// Currently we don't generate WM_UNICHAR, WM_SYSKEYDOWN, WM_SYSKEYUP, 
// WM_SYSDEADCHAR, WM_DEADCHAR nor do we handle these cases. I think that's ok.
//
// BUG: it seems that SendInput() and DispatchMessage() don't really work on a
// shared "queue". DispatchMessage seems to only ever get the last thing that
// SendInput sent or it misses an input entirely (the latter is obviously a leak
// condition, and it's caused when the user drags the window or clicks and
// holds it ... our DispatchMessage stops getting called, and I don't know
// where the keystrokes are going at that point, except onto our own deque).
// This is BAD because if the user drags the window when the injector thread
// is in the middle of one of its key-down/key-up sequences, then the filter
// here will only filter out half of it, and that could cause a leak of a
// character to the screen. If a shift-key is what is dropped, that could
// cause the filter to lose track of the intended shift key state and start
// dumping all of the generated keys onto the screen thereafter.
//
int WINAPI DispatchMessageHookProc(const MSG *lpmsg)
{
	// We have to track the state of the actual shift key, because when
	// pressed it will cause TranslateMessage() to change the WM_CHAR message
	// that it issues for our injected keystroke, and we will fail to filter
	// it.
	static bool bShiftIsPressed = false;
	static bool bInjectedKey = false;
	static bool bInjectedShiftIsPressed = false;
	static MSG actualMsg;

	switch(lpmsg->message)
	{
		// It's not a perfect solution, but it seems like we should pause the key
		// injector thread whenever we see a WM_NCLBUTTONDOWN message and until
		// we see a WM_LBUTTONUP message. These messages correspond with the user
		// moving or resizing the window and with clicking back in the main
		// window area. When the window is being moved or resized, it seems like
		// our calls to SendInput() (i.e. our keystroke injection) were going
		// into a black hole, and that broke our WM_CHAR injection-filter below.
	case WM_NCLBUTTONDOWN:
		OutputDebugString(L"Window held.\n");
		ResetEvent(ghFocusEvent);
		break;
	case WM_LBUTTONUP:
		OutputDebugString(L"Window released.\n");
		SetEvent(ghFocusEvent);
		break;

	case WM_CHAR:  // filter injected characters here
		//OutputDebugString(L"WM_CHAR message.\n");
		
		// Make a writable copy of the message, for correcting shift-key state:
		actualMsg = *lpmsg;

		// When we generated and injected keystrokes requiring Shift, we logged
		// shift-down and shift-up "events" in our queue, whether or not we
		// actually sent the Shift key. Before deciding what to filter, we must
		// know what the intended Shift state was of our injected key. That is
		// why we read any unprocessed Shift state events from our queue first.
		::EnterCriticalSection(&gQueueLock);
		while(!gInjectedKeyQueue.empty())
		{
			if(SHIFTKEY_DOWN == gInjectedKeyQueue.front())
				bInjectedShiftIsPressed = true;
			else if(SHIFTKEY_UP == gInjectedKeyQueue.front())
				bInjectedShiftIsPressed = false;
			else
				break;
			gInjectedKeyQueue.pop_front();
		}
		
		// The reliable way to track the "real" Shift state is not by tracking
		// WM_KEYDOWN/WM_KEYUP messages looking for VK_SHIFT, because that is 
		// (in my testing) not in sync with whatever state actually determines
		// whether a key is having Shift applied to it or not. Either use
		// GetKeyState (was falsely reporting VK_SHIFT?), or possibly GetAsyncKeyState (shown here).
		if(GetAsyncKeyState(VK_SHIFT) & 0x8000)
			bShiftIsPressed = true;

		// If our injected shift-keystrokes and the user's actual shift-keystrokes 
		// are out of synch, then we must correct the keystroke char now:
		if(bInjectedShiftIsPressed==false && bShiftIsPressed==true)
		{
			// An injected keystroke may have been accidentally capitalized by the user.
			// Account for that when checking if it is our last-injected keystroke:
			if(!gInjectedKeyQueue.empty() && (UndoShiftEffect(lpmsg->wParam) == gInjectedKeyQueue.front()))
			{
				gInjectedKeyQueue.pop_front();
				bInjectedKey = true;
			}
			else  // no worries, it was a user-pressed key. Let it through.
			{
				bInjectedKey = false;
			}
		}
		else if(bInjectedShiftIsPressed==true && bShiftIsPressed==false)
		{
			// If the observed char is from an injected keystroke:
			if(!gInjectedKeyQueue.empty() && lpmsg->wParam == gInjectedKeyQueue.front())
			{
				// no worries, it was an injected key and we'll filter it.
				gInjectedKeyQueue.pop_front();
				bInjectedKey = true;
			}
			else  // else it was a user's keystroke:
			{
				// A user-pressed keystroke was capitalized by an injected shift.
				// We should never reach this case, because it implies that 
				// SendInput allowed user keystrokes to intermingle between
				// our series of keystrokes ("Shift key-down",key-down,key-up,
				// "Shift key-up"), which it is not supposed to do.
				// If that ever happens, the only recourse is to force a 
				// down-capitalization of the typed character.
				OutputDebugString(L"SendInput failed to isolate our Shifted keystrokes? Attempting to fix.\n");
				actualMsg.wParam = UndoShiftEffect(lpmsg->wParam);
				bInjectedKey = false;
			}
		}
		else if(bInjectedShiftIsPressed==true && bShiftIsPressed==true)	// User-pressed shift-key state == injected shift-key state (both on)
		{
			// If the observed char is from an injected keystroke:
			if(!gInjectedKeyQueue.empty() && (lpmsg->wParam == gInjectedKeyQueue.front()))
			{
				gInjectedKeyQueue.pop_front();
				bInjectedKey = true;
			}
			else  // else it was a user's keystroke:
			{
				bInjectedKey = false;
			}
		}
		else 	// User-pressed shift-key state == injected shift-key state (both off)
		{
			// If the observed char is from an injected keystroke:
			if(!gInjectedKeyQueue.empty() && (lpmsg->wParam == gInjectedKeyQueue.front()))
			{
				gInjectedKeyQueue.pop_front();
				bInjectedKey = true;
			}
			else  // else it was a user's keystroke:
			{
				// This shouldn't be necessary, but because SendInput doesn't 
				// correctly isolate the use of Shift, we see that <0.5% of the
				// lowercase keys the user types may end up getting capitalized
				// EVEN THOUGH the GetKeyState and GetAsyncKeyState indicate that
				// there is no shift key active at the time.
				actualMsg.wParam = UndoShiftEffect(lpmsg->wParam);
				bInjectedKey = false;
			}
		}
		::LeaveCriticalSection(&gQueueLock);

		if(bInjectedKey)
		{
			OutputDebugString(L"Injected char (filtered).\n");
			return 0; // TODO: this gets ignored anyway but return something realistic.
		}
		else
		{
			swprintf_s(gtszString, (size_t)256, L"User key char pressed: %c.\n", lpmsg->wParam);
			OutputDebugString(gtszString);
			return pDmOrig((const MSG*)&actualMsg);
		}
		break;

	default:
		break;
	}

	// Call the original DispatchMessageW():
	return pDmOrig(lpmsg);
}


///////////////////////////////////////////////////////////////////////////////
// We must not inject keystroke noise to the Windows IO subsystem at times when
// our application does not have keyboard focus, because the keystroke noise
// can only be filtered out by our own code hooked into DispatchMessage().
// Therefore, we register for changes to keyboard focus, and enable and disable
// keystroke noise injection accordingly.
//
bool AddHookToKeyboardFocusChanges()
{
	HHOOK hhookFocusChange = NULL;  // a hook handle. Used for removing the hook later.

	// Create a manual-reset event object. It will be signaled when the hook
	// procedure sees a "focus gained" message, and non-signaled when the hook
	// procedure sees a "focus lost" message:
	ghFocusEvent = CreateEvent(NULL, TRUE, FALSE, L"FocusEvent");
	if(ghFocusEvent == NULL)
		return false;

	hhookFocusChange = SetWindowsHookEx(WH_CALLWNDPROC,  // alternately, WH_CALLWNDPROCRET
										(HOOKPROC)CallWindowProcHook,
										NULL,			// typically an HINSTANCE of hook DLL
										GetCurrentThreadId());	// creates a local hook
	if(hhookFocusChange == NULL)
		return false;

/*
	WARNING: I might want to register this hook from the injector thread that keeps running,
	rather than the DllMain thread which exits: "It is not specified in any doc, and I found
	this problem on several codes : For a local hook, if you install the hook from a thread
	to another thread in the process, the hook will stop to be called when the thread which 
	installed the hook exits (even if the thread on which you installed the hook is still running).
*/

	return true;
}

// If I can't create a local hook that detects focus changes, try SetWinEventHook() instead.
// Surely one of those events is a focus change. 
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd318066(v=vs.85).aspx

// If all else fails, I could maybe poll GetForegroundWindow() and/or GetFocus().
// If GetFocus() returns NULL then this app doesn't have keyboard focus


///////////////////////////////////////////////////////////////////////////////
// This is the routine that we register to be called on every call to a
// WindowProc in our application; we use it to catch WM_SETFOCUS and 
// WM_KILLFOCUS messages that indicate gaining or losing keyboard input focus.
// We can't catch these messages in our DispatchMessage() hook because unlike
// keyboard, mouse, paint, and timer messages, the focus messages are not
// posted to the message queue. Instead they are sent directly to WindowProc.
// But we can hook them here.
//
LRESULT WINAPI CallWindowProcHook(int nCode, WPARAM wParam, LPARAM lParam) 
{
	UINT message = ((CWPSTRUCT*)lParam)->message;

	switch(message)
	{
		/*
		// It seems like WM_ACTIVATE may not 1:1 correlate with gaining focus.
		case WM_ACTIVATE:
			OutputDebugString(L"Window activated.\n");
			// Enable keystroke injection engine:
			SetEvent(ghFocusEvent);
			break;
		*/
		// BUG: I need to find a way to pause keystroke generation when the user it moving or resizing the window.
		// I saw the following two messages in Spy++ but these cases in my code were not being hit.
		/*
		case WM_NCLBUTTONDOWN:
			OutputDebugString(L"Window held.\n");
			ResetEvent(ghFocusEvent);
			break;
		case WM_LBUTTONUP:
			OutputDebugString(L"Window released.\n");
			SetEvent(ghFocusEvent);
			break;
		*/
		case WM_SETFOCUS:
			OutputDebugString(L"Window focused.\n");
			// Enable keystroke injection engine:
			SetEvent(ghFocusEvent);
			break;
		case WM_KILLFOCUS:
			OutputDebugString(L"Window focus lost.\n");
			// Disable keystroke injection engine:
			ResetEvent(ghFocusEvent);
			break;
		default:
			break;
	}

	// CallNextHookEx calls the next hook in the chain.
	return CallNextHookEx(NULL, nCode, wParam, lParam); 
}


///////////////////////////////////////////////////////////////////////////////
// This routine is meant to run in its own thread. It generates random 
// keystrokes from the printable ASCII character range, and injects them into
// the keyboard input channel, to be filtered out again at the last possible
// point by our hook in DispatchMessage(). The injection of keystrokes starts
// when the parent process has input focus and stops when it does not.
//
DWORD WINAPI KeyInjectorThreadProc(LPVOID lpParameter)
{
	DWORD dwRandomMilliseconds = 1000;
	vector <INPUT> keyStrokes;

	WORD wKeyStroke = 0x0000;
	bool bInjectedShift = false;
	bool bPiggyBackedShift = false;

	int counter = -1;  // for debugging.
	
	// Initialize the PRNG with a pseudo-random seed:
	srand((int)time(NULL));

	// Lookup the correct mapping of characters to scan-codes:
	TCHAR buff[120];
	GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, buff, sizeof(buff));
	HKL hKeyboardLayout = ::LoadKeyboardLayout(buff, KLF_ACTIVATE);

	// In an endless loop, inject keystrokes, but wait until we have the keyboard focus:
	while(!WaitForSingleObject(ghFocusEvent, INFINITE))
	{
		// Bug: this is not a good random distribution; results in mostly letters near the bottom of the range you provide.
		// The random generation formula goes like this: numberInRange = rand() % range + rangeBase;
		wKeyStroke = (WORD)(rand() % 0x5D + 0x21); // the printable ASCII character range, 0x21 - 0x7E
//		wKeyStroke = (WORD)(rand() % 0x19 + 0x61);  // just lowercase letters, 0x61 - 0x7A
//		wKeyStroke = (WORD)((++counter) % 0x5D + 0x21); // just count alphabet upward and start over in a loop
		
		bInjectedShift = false;
		bPiggyBackedShift = false;
		keyStrokes.clear();
		INPUT Event = { 0 };
		const SHORT Vk = VkKeyScanEx(wKeyStroke, hKeyboardLayout);
		const UINT VKey = ::MapVirtualKey(LOBYTE(Vk), MAPVK_VK_TO_VSC);

		// TODO: do I need to be zeroing Event and re-setting the same fields each time?

		if(HIBYTE(Vk)==1) // checks if shift needs to be pressed.
		{
			// The Shift key-down:
			::ZeroMemory(&Event, sizeof(Event));
			Event.type = INPUT_KEYBOARD;
			Event.ki.dwFlags = KEYEVENTF_SCANCODE;
			Event.ki.wScan = ::MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
			keyStrokes.push_back(Event);
			bInjectedShift = true;
		}

		// The key-down:
		::ZeroMemory(&Event, sizeof(Event));
		Event.type = INPUT_KEYBOARD;
		Event.ki.dwFlags = KEYEVENTF_SCANCODE;
		Event.ki.wScan = VKey;
		keyStrokes.push_back(Event);

		// The key-up:
		::ZeroMemory(&Event, sizeof(Event));
		Event.type = INPUT_KEYBOARD;
		Event.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
		Event.ki.wScan = VKey;
		keyStrokes.push_back(Event);

		if(bInjectedShift) // release shift if it was needed.
		{
			// The Shift key-up:
			::ZeroMemory(&Event, sizeof(Event));
			Event.type = INPUT_KEYBOARD;
			Event.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
			Event.ki.wScan = ::MapVirtualKey(VK_LSHIFT, MAPVK_VK_TO_VSC);
			keyStrokes.push_back(Event);
		}

		::EnterCriticalSection(&gQueueLock);
		/* ----- DEBUG ----- //
		if(!gInjectedKeyQueue.empty())
		{
			int queueSize = gInjectedKeyQueue.size();
			if(queueSize > 1)
			{
				swprintf_s(gtszString, (size_t)256, L"Injected key queue is growing. Size: %i\n", queueSize);
				OutputDebugString(gtszString);
			}
		}
		// ----- END DEBUG ----- */

		// Communicate the injected keystrokes to our DispatchMessage() filter:
		if(HIBYTE(Vk)==1)  //we must track the fact that the next key is shifted, even if we piggybacked on a user's shift
			gInjectedKeyQueue.push_back(SHIFTKEY_DOWN);
		gInjectedKeyQueue.push_back(wKeyStroke);
		if(HIBYTE(Vk)==1)
			gInjectedKeyQueue.push_back(SHIFTKEY_UP);

		// Now send the keystrokes to the Windows message queue. Because the use of
		// Shift is problematic when interleaved with user input (see our report for
		// more detail) we always send shifted keys by using the batched input
		// mode of SendInput where we send both pairs of down/up keystrokes at once:
		if(HIBYTE(Vk)==1)
		{
			// First wait for the user to let go of Shift, or we might screw them up:
			while((GetAsyncKeyState(VK_SHIFT) & 0x8000))
			{
				::LeaveCriticalSection(&gQueueLock);
				Sleep(5);
				::EnterCriticalSection(&gQueueLock);
			}
			if(SendInput(keyStrokes.size(), keyStrokes.data(), sizeof(INPUT)) == 0)
			{
				OutputDebugString(L"SendInput() failed.\n");
			}
			else
			{
				swprintf_s(gtszString, (size_t)256, L"Injected key: %c\n", wKeyStroke);
				OutputDebugString(gtszString);
			}
		}
		// For unshifted keys, send the pair of down/up keystrokes individually and slower:
		else
		{
			for(unsigned int i=0; i < keyStrokes.size(); i++)
			{
				// First wait for the user to let go of Shift, or they will screw us up:
				while((GetAsyncKeyState(VK_SHIFT) & 0x8000))
				{
					::LeaveCriticalSection(&gQueueLock);
					Sleep(5);
					::EnterCriticalSection(&gQueueLock);
				}

				if(SendInput(1, &(keyStrokes[i]), sizeof(INPUT)) == 0)
				{
					OutputDebugString(L"SendInput() failed.\n");
				}
				else
				{
					swprintf_s(gtszString, (size_t)256, L"Injected key: %c %s\n", 
						wKeyStroke, (keyStrokes[i].ki.dwFlags & KEYEVENTF_KEYUP)?L"up":L"down");
					OutputDebugString(gtszString);
				}

				// Sleep a random time between 10ms and 20ms (approximating user typing dynamics):
				dwRandomMilliseconds = rand() % 10 + 10;
				Sleep(dwRandomMilliseconds);
			}
		}

		::LeaveCriticalSection(&gQueueLock);

		// Sleep a random time between 25ms and 90ms (approximating user typing dynamics):
		dwRandomMilliseconds = rand() % 55 + 25;
		Sleep(dwRandomMilliseconds);
	}

	return 0;
}


///////////////////////////////////////////////////////////////////////////////
// This routine does the reverse of what holding "shift" does to an ASCII
// character value of keyboard input. You know what would be better, is a
// lookup table. Also it needs to be tested when you are using a different
// keyboard layout.
//
WORD UndoShiftEffect(const WORD wKey)
{
	WORD wUnshiftedKey;

	if(wKey == 0x21 ||  
		(wKey >= 0x23 && wKey <= 0x25))  // certain ASCII digits and punctuation
	{
		wUnshiftedKey = wKey + 0x10;	// e.g., turn 0x21 = '!' into 0x31 = '1'
	}
	else if(wKey == 0x22)
	{
		wUnshiftedKey = 0x27;	// turn '"' into '''
	}
	else if(wKey == 0x26)
	{
		wUnshiftedKey = 0x37;	// turn '&' into '7'
	}
	else if(wKey == 0x28)
	{
		wUnshiftedKey = 0x39;	// turn '(' into '9'
	}
	else if(wKey == 0x29)
	{
		wUnshiftedKey = 0x30;  // turn ')' into '0'
	}
	else if(wKey == 0x2a)
	{
		wUnshiftedKey = 0x38;	// turn '*' into '8'
	}
	else if(wKey == 0x2B)
	{
		wUnshiftedKey = 0x3D;	// turn '+' into '='
	}
	else if(wKey == 0x3A)
	{
		wUnshiftedKey = 0x3B;	// turn ':' into ';'
	}
	else if(wKey == 0x3C || wKey == 0x3E || wKey == 0x3F)  // certain other punctuation
	{
		wUnshiftedKey = wKey - 0x10;	// e.g., turn 0x3C = '<' into 0x2C = ','
	}
	else if(wKey == 0x40)
	{
		wUnshiftedKey = 0x32;		// turn '@' into '2'
	}
	else if(wKey == 0x5E)
	{
		wUnshiftedKey = 0x36;		// turn '^' into '6'
	}
	else if(wKey == 0x5F)
	{
		wUnshiftedKey = 0x2D;		// turn '_' into '-'
	}
	else if(wKey >= 0x41 && wKey <= 0x5A)  // the ASCII capital letters
	{
		wUnshiftedKey = wKey + 0x20;	// e.g., turn 0x41 = 'A' into 0x61 = 'a'
	}
	else if(wKey >= 0x7b && wKey <= 0x7d )  // curly braces / brackets  {|} / [\]
	{
		wUnshiftedKey = wKey - 0x20;
	}
	else if(wKey == 0x7E) // tick mark/tilde
	{
		wUnshiftedKey = 0x60;
	}
	else
	{
		// all other value ranges for wKey are not shifted ASCII and can be ignored. Probably.
		wUnshiftedKey = wKey;
		OutputDebugString(L"UndoShiftEffect: already wasn't shifted.\n");
	}

	return wUnshiftedKey;
}