Results 1 to 24 of 24

Thread: Subclassing SAS Window

  1. #1

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Subclassing SAS Window

    Hi, I have recently been asked by a neighbor of mine if I could create a security application with methods to secure a windows desktop. Now I know there are a billion ways to do it in the registry but I figure it would be a great learning experience to see how well I can do this programmatically.

    Now I have created LowLevelKey hooks to disable common keys like Alt+Tab and window key functions but the Ctrl Alt Del key combo had me confused at first. Well with research I have found that this Hotkey is monitored by Winlogon.exe and specifically its SAS Window.

    So I spent a few days thinking out various methods to handle this. 1st I was thinking about looking around for a Keyfilter hook driver (but since I am not familiar with C++ that is kind of rulled out)
    Next I looked in the MS Database and looked around on rebuilding the GINA. But this method is really too messy, too complex and I think would be a lot more work. (Not to mention dangerous)

    So my final solution was the SAS Window Subclass. I found a good exmaple here: http://www.codeproject.com/win32/AntonioWinLock.asp yet since i dont know C++ it has been a difficult past weeks to try and convert into vb6. (These memory API functions kill me hah) anyways..

    So I was wondering if anyone has done this in VB6 yet? If so do you care to share your module or function?

    Most likely, I am thinking that a lot of people have probably not since the real internals of windows are coded mostly by C++ people.

    So my other question is, does anyone know of a C++ or VC++ to VB6 to VB.net converter?
    This would be the ultimate find if anyone knows of any, since this would greatly help me out in other project where I attempt to analyze C code and manually code and convert it line by line, which for myself seems to have a 90% fail rate. (They don't have to be free, I would probably pay for something that valuable)





    Summary:
    1. Has anyone ever Subclassed the Winlogon.exe SAS Window in VB?
    2. If not would anyone be willing to give me a hand from AIM or e-mail or something in the process of converting this example to VB.
    3. Does anyone know of a C to VB converter?

  2. #2
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    By the looks of it, you have no choice, but to use some c++ because it requires a win32 dll to subclass an external process. The author in that website you mentioned did mention using WriteProcessMemory, but like he said, its not very easy. I'd ususally use the SetWindowsHookEx method or the CreateRemoteThread when trying to inject code to external processes.

  3. #3

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Do you have any examples posted like at pscode or something where you demonstrate this remote code injection. Because once I get the code injected and create a remote thread, then its half the work I have to do.

    I then have to figure out how C++ uses hooks by comparing it with C++ Keyhook examples to my VB Keyhook examples.

    But yes, your code could help me a lot, because that C++ code posted uses the code injection method which I have yet to find an example of in VB. I mean I have seen many with the dll injection, but this is code injection. So I would love to take a look.

    Thanks.

  4. #4
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    Check this thread:

    http://www.vbforums.com/showthread.p...teremotethread

    it should give you some ideas.

  5. #5

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Interesting code.

    This does seem to help out with some of the coding. Yet, how can I create a remote thread through injection of CODE instead of a DLL.
    Apparently that is what this guy does, but I dont quite understand how it is possible.

    Any Ideas?

  6. #6
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    The example given uses a dll actually. Let me explain how all this works. Basically, for you to subclass the external application, you need to make it call SetWindowLong() with the GWL_WNDPROC (as one of its parameter) in the external application's process. I'm assuming you already know that to subclass your own program, your program would call SetWindowLong as well. So it pretty much work the same way. The question is, how do you make the external application call that function. This is where CreateRemoteThread and the DLL comes in.

    CreateRemoteThread uses (as one of its parameter) a pointer to its callback function. This callback is called everytime CreateRemoteThread is called. In our case, we'll make it point to the LoadLibrary() instead. Another parameter of CreateRemoteThread is the parameter that will be used by the callback function (which is LoadLibrary in our case). That parameter will be the pointer to the path of the DLL (which will contain the code to be injected). The string value of this path must be in the process memory of the external application, and this is done by using WriteProcessMemory(). THen when you call CreateRemoteThread, it will call the callback function, which will be LoadLibrary() and the call to LoadLibrary will cause the DLL to be loaded in the external process. Notice that the dll has a Dllmain() procedure. Everytime a dll is loaded, the Dllmain() is called. And anything called inside Dllmain() is now being CALLED BY THE EXTERNAL PROCESS. And that's pretty much how it works. If you look at http://www.vbforums.com/showthread.p...teremotethread again, you should see that it does how I explained it. You just need to make changes to the dll so that it suites your need.

  7. #7

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Quote Originally Posted by benmartin101
    The example given uses a dll actually. Let me explain how all this works. Basically, for you to subclass the external application, you need to make it call SetWindowLong() with the GWL_WNDPROC (as one of its parameter) in the external application's process. I'm assuming you already know that to subclass your own program, your program would call SetWindowLong as well. So it pretty much work the same way. The question is, how do you make the external application call that function. This is where CreateRemoteThread and the DLL comes in.

    CreateRemoteThread uses (as one of its parameter) a pointer to its callback function. This callback is called everytime CreateRemoteThread is called. In our case, we'll make it point to the LoadLibrary() instead. Another parameter of CreateRemoteThread is the parameter that will be used by the callback function (which is LoadLibrary in our case). That parameter will be the pointer to the path of the DLL (which will contain the code to be injected). The string value of this path must be in the process memory of the external application, and this is done by using WriteProcessMemory(). THen when you call CreateRemoteThread, it will call the callback function, which will be LoadLibrary() and the call to LoadLibrary will cause the DLL to be loaded in the external process. Notice that the dll has a Dllmain() procedure. Everytime a dll is loaded, the Dllmain() is called. And anything called inside Dllmain() is now being CALLED BY THE EXTERNAL PROCESS. And that's pretty much how it works. If you look at http://www.vbforums.com/showthread.p...teremotethread again, you should see that it does how I explained it. You just need to make changes to the dll so that it suites your need.

    Ok well that does help make some more sence of it, although I am really confused on how this guy does it with a specific peice of code instead of a dll.
    It looks like he is injecting a data structure and calling that, but how is this possible?

    I actually just checked to verify that this method does work by not injecting any dll into the process. I mointored winlogon.exe's open dlls and used the vb app that he created that imports the function from the dll. I initially was, for a minute, thinking this dll is what is injected. Although it is for sure not the case. He did this purely by code injection instead of dll injection. (Which is better in my opinion since it doesn't need to rely on a dll to function properly)


    So I have been trying to analyze this code down here:

    Look at the very first block of code:
    Apparently DataLocal is the Structure INJDATA and this is what he is injecting as shown here:
    Code:
    	
    
    		// Initialize INJDATA for GetSASWnd() call
    		strcpy(DataLocal.szClassName, "SAS Window class");
    		strcpy(DataLocal.szWindowName, "SAS window");
    		DataLocal.fnFindWindow = (FINDWINDOW) GetProcAddress(hUser32, "FindWindowA");
    		if (DataLocal.fnFindWindow == NULL)
    			__leave;
    
    		// Allocate memory in the remote process and write a copy of initialized INJDATA into it
    		size = sizeof(INJDATA);
    		pDataRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);		
    		if (!pDataRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pDataRemote, &DataLocal, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    
    		// Allocate memory in remote process and write a copy of GetSASWnd() into it
    		size = (PBYTE)AfterGetSASWnd - (PBYTE)GetSASWnd;
    		pGetSASWndRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);		
    		if (!pGetSASWndRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pGetSASWndRemote, &GetSASWnd, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    		
    		// Start execution of remote GetSASWnd()
    		hThread = CreateRemoteThread(hProcess, 
    									 NULL, 
    									 0, 
    									 (LPTHREAD_START_ROUTINE) pGetSASWndRemote,
    									 pDataRemote, 
    									 0 , 
    									 &dwThreadId);



    Here is the structure:
    Code:
    typedef struct {
    	SETWINDOWLONG	fnSetWindowLong;	// Addr. of SetWindowLong()
    	CALLWINDOWPROC	fnCallWindowProc;	// Addr. of CallWindowProc()
    	FINDWINDOW		fnFindWindow;		// Addr. of FindWindow()
    	char			szClassName[50];	// Class name = "SAS Window class"
    	char			szWindowName[50];	// Window name = "SAS window"
    	HWND			hwnd;				// Window handle of injected process
    	WNDPROC			fnSASWndProc;		// Addr. of remote SASWindowProc
    	WNDPROC 		fnOldSASWndProc;	// Addr. of old SASWindowProc
    } INJDATA, *PINJDATA;
    This structure seems to be what he is injecting as the "code"

    Although,
    I really have no clue how to do this in VB6 or I would just do this same, but I cant because I am not sure of the:
    1. Addr. of SetWindowLong
    2. Addr. of CallWindowProc
    3. Addr. of FindWindow
    4. Addr. of remote SASWindowProc.
    5. Addr. of old SASWindowProc


    By best guess would be something like:
    Code:
    Private Type DataRemote
    	aCallWinProc as Long		'Address of CallWindowProc
    	aFindWindow as Long		'Address of  FindWindow
    	sClassName as String		'SAS Window Class Name
    	sWindowName as String		'SAS Window Text Name
    	hWindow as Long			'winlogon.exe Window Handle
    	aSASWindowProc as Long		'Address of remote SASWindowProc
    	aSASOldWindowProc as Long	'Address of old SASWindowProc
    End Type
    
    
    ----------------------------------------------------------
    '(After the handle has been opned......)
    
    
    'Initialize INJDATA for GetSASWnd() call
    Dim  tDataRemote as DataRemote
    
     tDataRemote.sClassName = "SAS Window class"
     tDataRemote.sWindowName = "SAS window"
     tDataRemote.aFindWindow = GetAddressofFunction (AddressOf FindWindowA() )
    
    'Allocate memory in the remote process and write a copy of initialized INJDATA into it
    Dim lSize as Long
    
    Dim pDataRemote as Long
    Dim pGetSASWndRemote as Long
    
    Dim lNumBytesCopied as Long
    Dim hThread as Long
    
    
    
    'Allocate memory in the remote process and write a copy of initialized INJDATA into it
    lSize = Len(tDataRemote)
    pDataRemote = VirtualAllocEx(hProcess, 0, lSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    Call WriteProcessMemory(hProcess, pDataRemote, VarPtr(DataLocal), lSize, VarPtr(lNumBytesCopied))
    
    
    'Allocate memory in remote process and write a copy of GetSASWnd() into it
    lSize = AfterGetSASWnd - GetSASWnd
    pGetSASWndRemote = VirtualAllocEx(hProcess, 0, lSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    Call WriteProcessMemory(hProcess, pGetSASWndRemote, VarPtr(GetSASWnd), lSize, VarPtr(dwNumBytesCopied))
    
    
    'Start execution of remote GetSASWnd()
    hThread = CreateRemoteThread(hProcess, vbNull, 0, pGetSASWndRemote, pDataRemote, 0, VarPtr(dwThreadId)
    									  
    
    ----------------------------------------------------------
    
    Private Function GetAddressofFunction(ByVal lAddress As Long) As Long
    	 'This function allows you to assign a function pointer to a vaiable:
    	'A dummy procedure that receives and returns the value of the AddressOf operator.
    	'This workaround is needed as you can't assign AddressOf directly to a member of a user-defined type, but you can assign it to another long and use that instead!
    
    	GetAddressofFunction = lAddress
    End Function
    
    
    '**************************************************************
    ' * Return the window handle of the remote process (Winlogon). *
    ' **************************************************************
    
    Private Function GetSASWnd
    	Dim  tDataRemote as DataRemote
    	GetSASwnd = FindWindowA (tDataRemote.szClassName, tDataRemote.szWindowName
    End Function
    
    Private Function AfterGetSASWnd
    	AfterGetSASWnd = 4
    End Function

    Am I on the right track or way off???


    Please Help...
    Last edited by altf4; Apr 26th, 2007 at 02:39 PM.

  8. #8

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Opps sorrry I forgot to post the Inject Code:


    PART 1:
    Code:
    ////////////////////////////////////////////// Inject Code ////////////////////////////////////////
    
    /**************************************************************************************************************
     * Some tips when using CreateRemoteThread()/WriteProcessMemory().                                            *
     *                                                                                                            *
     * 1. Analise generated code.                                                                                 *
     *    Check injected functions for absolute addressing (calls, jumps, data references, ...)                   * 
     *    and other generated code that shouldn't be there.                                                       *
     *    Use:                                                                                                    *
     *    A) Project\Settings\C/C++\Listing Files\Listing file type=Assembly, Machine Code, and Source.           *
     *    B) A disassembler (wdasm32).                                                                            *
     *    C) A debugger (softice).                                                                                *
     *                                                                                                            *
     * 2. Turn off stack probes.                                                                                  *
     *    Check for __chkstk() references in the listing files.                                                   *
     *    A) Use #pragma check_stack(off). [DOESN'T SEEMS TO WORK ?!?]                                            *
     *    B) Use less than 4K of local variables.                                                                 *
     *    C) Augment the stack size: /Gs size (Project\Settings\C/C++\ProjectOptions)                             *
     *                                                                                                            *
     * 3. Remove the /GZ switch in the debug build.                                                               *
     *    Check for __chkesp() references in the listing files.                                                   *
     *    A) Project\Settings\C/C++\Project Options                                                               *
     *                                                                                                            *
     * 4. Disable incremental compilation (/Gi).                                                                  *
     *    A) Use #pragma comment(linker, "/INCREMENTAL:NO")                                                       *
     *    B) Remove the /Gi switch (Project\Settings\C/C++\Customize\Enable incremental compilation=Off           *
     *    C) Declare the functions as static.                                                                     *
     *                                                                                                            *
     * 5. Don't let optimization screw your code.                                                                 *
     *    A) Turn off optimization (Project\Settings\C/C++\General\Optimizations=Disable(Debug)                   *
     *    B) Use #pragma optimize("", off)                                                                        *
     *    C) Don't write functions with the same prototype (e.g. AfterFuncX()). Let them return different values. *
     *                                                                                                            *
     * 6. Split switch() statements in 3 cases maximum, or use if/then/else.                                      *
     *                                                                                                            *
     * 7. Don't call any functions besides those in KERNEL32.LL and USER32.DLL (USER32.DLL isn't garanted to be   *
     *    mapped into every process).                                                                             *
     *    Use LoadLibrary()/GetProcAddress if you need functions from other libraries.                            *
     *                                                                                                            *
     * 8. Don't use any static strings.                                                                           *
     *    Pass them in INJDATA.                                                                                   *
     *                                                                                                            *
     * 9. Don't call any function directly.                                                                       *
     *    Copy each routine to the remote process individually and supply their addresses in INJDATA.             *
     *                                                                                                            *
     * 10. Good luck.                                                                                             *
     *     If you analise the generated code (using a disassembler) you should catch any errors before executing  *
     *     the code (and crashing the process !).                                                                 *
     **************************************************************************************************************/  
    
    #pragma comment(linker, "/INCREMENTAL:NO")	// Turns off incremental linking
    
    // Global variables
    DWORD	PID;						// PID of injected process
    BYTE	*pDataRemote;				// Address of INJDATA in the remote process
    BYTE	*pSASWinProcRemote;			// The address of SASWindowProc() in the remote process
    
    #define	DUMMY_ADDR	0x12345678		// Dummy addr of INJDATA
    
    // INJDATA: Memory block passed to each remote injected function.
    // We pass every function address or string data in this block.
    typedef LONG		(WINAPI *SETWINDOWLONG)	  (HWND, int, LONG); 
    typedef LRESULT		(WINAPI *CALLWINDOWPROC)  (WNDPROC, HWND, UINT, WPARAM, LPARAM);
    typedef HWND		(WINAPI *FINDWINDOW)	  (LPCTSTR, LPCTSTR);
    
    typedef struct {
    	SETWINDOWLONG	fnSetWindowLong;	// Addr. of SetWindowLong()
    	CALLWINDOWPROC	fnCallWindowProc;	// Addr. of CallWindowProc()
    	FINDWINDOW		fnFindWindow;		// Addr. of FindWindow()
    	char			szClassName[50];	// Class name = "SAS Window class"
    	char			szWindowName[50];	// Window name = "SAS window"
    	HWND			hwnd;				// Window handle of injected process
    	WNDPROC			fnSASWndProc;		// Addr. of remote SASWindowProc
    	WNDPROC 		fnOldSASWndProc;	// Addr. of old SASWindowProc
    } INJDATA, *PINJDATA;
    
    
    /*****************************************************************
     * Subclassed window procedure handler for the injected process. *
     *****************************************************************/
    
    #pragma optimize("", off)
    #pragma check_stack(off)
    
    static LRESULT CALLBACK SASWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	// INJDATA pointer. 
    	// Must be patched at runtime !
    	INJDATA* pData = (INJDATA *)DUMMY_ADDR;
    
    	if (uMsg == WM_HOTKEY)
    	{
    		// Ctrl+Alt+Del
    		if (lParam == MAKELONG(MOD_CONTROL | MOD_ALT, VK_DELETE))
    			return 1;
    
    		// Ctrl+Shift+Esc
    		if (lParam == MAKELONG(MOD_CONTROL | MOD_SHIFT, VK_ESCAPE))
    			return 1;
    	}
    
    	// Call the original window procedure
    	return pData->fnCallWindowProc(pData->fnOldSASWndProc, hwnd, uMsg, wParam, lParam);		
    }
    
    
    static int AfterSASWindowProc(void) {return 1;}
    
    
    
    /*************************************************
     * Subclass the remote process window procedure. *
     * Return: 0=failure, 1=success                  *
     *************************************************/
    
    #pragma optimize("", off)
    #pragma check_stack(off)
    
    static DWORD WINAPI InjectFunc (INJDATA *pData) 
    {
    	// Subclass window procedure
    	pData->fnOldSASWndProc = (WNDPROC) pData->fnSetWindowLong (pData->hwnd, GWL_WNDPROC, (long)pData->fnSASWndProc);	
    
    	return (pData->fnOldSASWndProc != NULL);
    }
    
    
    static int AfterInjectFunc(void) {return 2;}
    
    
    /***********************************************************
     * Restore the subclassed remote process window procedure. *
     * Return: 0=failure, 1=success                            *
     ***********************************************************/
    
    #pragma optimize("", off)
    #pragma check_stack(off)
    
    static DWORD WINAPI EjectFunc (INJDATA *pData) 
    {
    	return (pData->fnSetWindowLong(pData->hwnd, GWL_WNDPROC, (long)pData->fnOldSASWndProc) != 0);
    }
    
    
    static int AfterEjectFunc(void) {return 3;}
    
    
    
    /**************************************************************
     * Return the window handle of the remote process (Winlogon). *
     **************************************************************/
    
    #pragma optimize("", off)
    #pragma check_stack(off)
    
    static HWND WINAPI GetSASWnd (INJDATA *pData) 
    {
    	return (pData->fnFindWindow(pData->szClassName, pData->szWindowName));
    }
    
    
    static int AfterGetSASWnd(void) {return 4;}
    Last edited by altf4; Apr 26th, 2007 at 02:48 PM.

  9. #9

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    PART 2:
    Code:
    ***************************************************************************
     * Copies InjectFunc(), GetSASWnd() , SASWindowProc() and INJDATA to the   *
     * remote process.                                                         *
     * Starts the execution of the remote InjectFunc(), which subclasses the   *
     * remote process default window procedure handler.                        *
     *                                                                         *
     * Return value: 0=failure, 1=success                                      *
     ***************************************************************************/
    
    int InjectCode ()
    {
    	HANDLE		hProcess = 0;			// Process handle
    	HMODULE		hUser32  = 0;			// Handle of user32.dll
    	BYTE		*pCodeRemote;			// Address of InjectFunc() in the remote process.
    	BYTE		*pGetSASWndRemote;		// Address of GetSASWnd() in the remote process.
    	HANDLE		hThread	= 0;			// The handle and ID of the thread executing
    	DWORD		dwThreadId = 0;			//   the remote InjectFunc().
    	INJDATA		DataLocal;				// INJDATA structure
    	BOOL		fUnicode;				// TRUE if remote process is Unicode
    	int			nSuccess = 0;			// Subclassing succeded?
    	DWORD		dwNumBytesCopied = 0;	// Number of bytes written to the remote process.
    	DWORD		size;					// Calculated function size (= AfterFunc() - Func())
    	int			SearchSize;				// SASWindowProc() dummy addr. search size
    	int			nDummyOffset;			// Offset in SASWindowProc() of dummy addr.
    	BOOL		FoundDummyAddr;			// Dummy INJDATA reference found in SASWindowProc() ?
    	HWND		hSASWnd;				// Window handle of Winlogon process
    	BYTE		*p;
    
    	// Enable Debug privilege (needed for some processes)
        if (!EnablePrivilege(SE_DEBUG_NAME, TRUE))
    		return 0;
    
    	// Get handle of "USER32.DLL"
    	hUser32 = GetModuleHandle("user32");
    	if (!hUser32)
    		return 0;
    
    	// Get remote process ID
        PID = GetPIDFromName(szProcessName);
        if (PID == -1)
    		return 0;
    
    	// Open remote process
    	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	if (!hProcess)
    		return 0;
    
    	__try 
    	{
    		// Initialize INJDATA for GetSASWnd() call
    		strcpy(DataLocal.szClassName, "SAS Window class");
    		strcpy(DataLocal.szWindowName, "SAS window");
    		DataLocal.fnFindWindow = (FINDWINDOW) GetProcAddress(hUser32, "FindWindowA");
    		if (DataLocal.fnFindWindow == NULL)
    			__leave;
    
    		// Allocate memory in the remote process and write a copy of initialized INJDATA into it
    		size = sizeof(INJDATA);
    		pDataRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);		
    		if (!pDataRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pDataRemote, &DataLocal, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    
    		// Allocate memory in remote process and write a copy of GetSASWnd() into it
    		size = (PBYTE)AfterGetSASWnd - (PBYTE)GetSASWnd;
    		pGetSASWndRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);		
    		if (!pGetSASWndRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pGetSASWndRemote, &GetSASWnd, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    		
    		// Start execution of remote GetSASWnd()
    		hThread = CreateRemoteThread(hProcess, 
    									 NULL, 
    									 0, 
    									 (LPTHREAD_START_ROUTINE) pGetSASWndRemote,
    									 pDataRemote, 
    									 0 , 
    									 &dwThreadId);
    		// Failed
    		if (!hThread)
    			__leave;
    
    		// Wait for GetSASWnd() to terminate and get return code (SAS Wnd handle)
    		WaitForSingleObject(hThread, INFINITE);
    		GetExitCodeThread(hThread, (PDWORD) &hSASWnd);
    
    		// Didn't found "SAS window"
    		if (!hSASWnd)
    			__leave;
    
    		// Cleanup
    		VirtualFreeEx(hProcess, pGetSASWndRemote, 0, MEM_RELEASE);
    		VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);
    		pGetSASWndRemote = NULL;
    		pDataRemote = NULL;
    		
    		// Allocate memory in remote process and write a copy of SASWindowProc() into it
    		size = (PBYTE)AfterSASWindowProc - (PBYTE)SASWindowProc;
    		pSASWinProcRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);		
    		if (!pSASWinProcRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pSASWinProcRemote, &SASWindowProc, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    
    		// Is remote process unicode ?
    		fUnicode = IsWindowUnicode(hSASWnd);
    
    		// Initialize the INJDATA structure
    		DataLocal.fnSetWindowLong = (SETWINDOWLONG)  GetProcAddress(hUser32, fUnicode ? "SetWindowLongW" : "SetWindowLongA");
    		DataLocal.fnCallWindowProc = (CALLWINDOWPROC) GetProcAddress(hUser32, fUnicode ? "CallWindowProcW": "CallWindowProcA");
    		DataLocal.fnSASWndProc = (WNDPROC) pSASWinProcRemote;
    		DataLocal.hwnd = hSASWnd;
    
    		if (DataLocal.fnSetWindowLong  == NULL || 			
    			DataLocal.fnCallWindowProc == NULL)
    		{
    			__leave;		
    		}
    
    		// Allocate memory in the remote process and write a copy of initialized INJDATA into it
    		size = sizeof(INJDATA);
    		pDataRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);		
    		if (!pDataRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pDataRemote, &DataLocal, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    
    		// Change dummy INJDATA address in SASWindowProc() by the real INJDATA pointer
    		p = (PBYTE)&SASWindowProc;
    		size = (PBYTE)AfterSASWindowProc - (PBYTE)SASWindowProc;
    		SearchSize = size - sizeof(DWORD) + 1;
    		FoundDummyAddr = FALSE;
    
    		for (; SearchSize > 0; p++, SearchSize--)
    		{
    			if (*(DWORD *)p == DUMMY_ADDR)	// Found 
    			{
    				nDummyOffset = p - (PBYTE)&SASWindowProc; 
    				if (!WriteProcessMemory(hProcess, pSASWinProcRemote + nDummyOffset, &pDataRemote, sizeof(pDataRemote), &dwNumBytesCopied) ||
    					dwNumBytesCopied != sizeof(pDataRemote))
    				{
    					__leave;
    				}
    				FoundDummyAddr = TRUE;
    				break;
    			}
    		}
    
    		// Couldn't change the dummy INJDATA addr. by the real addr. in SASWindowProc() !?!
    		// Don't execute the remote copy of SASWindowProc() because the pData pointer is invalid !
    		if (!FoundDummyAddr)
    		{
    			__leave;
    		}
    
    		// Allocate memory in the remote process and write a copy of InjectFunc() to the allocated memory
    		size = (PBYTE)AfterInjectFunc - (PBYTE)InjectFunc;
    		pCodeRemote = (PBYTE) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    		if (!pCodeRemote)
    			__leave;
    		if (!WriteProcessMemory(hProcess, pCodeRemote, &InjectFunc, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    			__leave;
    
    		// Start execution of remote InjectFunc()
    		hThread = CreateRemoteThread(hProcess, 
    									 NULL, 
    									 0, 
    									 (LPTHREAD_START_ROUTINE) pCodeRemote,
    									 pDataRemote, 
    									 0 , 
    									 &dwThreadId);
    		if (!hThread)
    			__leave;
    
    		// Wait for InjectFunc() to terminate and get return code
    		WaitForSingleObject(hThread, INFINITE);
    		GetExitCodeThread(hThread, (PDWORD) &nSuccess);
    
    		// InjectFunc() successfull
    //		if (nSuccess)
    //			MessageBeep(0);
    	}
    
    	__finally 
    	{
    		// Failed ?
    		if (!nSuccess)
    		{
    			// Release memory for INJDATA and SASWindowProc()
    			if (pDataRemote)
    				VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);
    			if (pSASWinProcRemote)
    				VirtualFreeEx(hProcess, pSASWinProcRemote, 0, MEM_RELEASE);
    			pDataRemote = NULL;
    			pSASWinProcRemote = NULL;
    		}
    
    		// Release remote GetSASWnd()
    		if (pGetSASWndRemote)	
    			VirtualFreeEx(hProcess, pGetSASWndRemote, 0, MEM_RELEASE);
    
    		// Release remote InjectFunc() (no longer needed)
    		if (pCodeRemote)	
    			VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
    
    		if (hThread)			
    			CloseHandle(hThread);
    
    	}
    
    	CloseHandle(hProcess);
    
    	// Disable the DEBUG privilege
    	EnablePrivilege(SE_DEBUG_NAME, FALSE);
    
    	return nSuccess;	// 0=failure; 1=success
    }

  10. #10

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    PART 3:
    Code:
    /**********************************************************************
     * Copies EjectFunc() to the remote process and starts its execution. *
     * The remote EjectFunc() restores the old window procedure.          *
     *                                                                    *
     *	Return value: 0=failure, 1=success                                *
     **********************************************************************/
    
    int EjectCode ()
    {
    	HANDLE		hProcess;				// Remote process handle
    	DWORD		*pCodeRemote;			// Address of EjectFunc() in the remote process
    	HANDLE		hThread = NULL;			// The handle and ID of the thread executing
    	DWORD		dwThreadId = 0;			//   the remote EjectFunc().
    	int			nSuccess	= 0;		// EjectFunc() success ?
    	DWORD		dwNumBytesCopied = 0;	// Number of bytes written to the remote process. 
    	DWORD		size;					// Calculated function size (= AfterFunc() - Func())
    
    	// Enable Debug privilege (needed for some processes)
    	EnablePrivilege(SE_DEBUG_NAME, TRUE);
    
    	// Remote INDATA and SASWindowProc() must exist 
    	if (!pDataRemote || !pSASWinProcRemote)
    		return 0;
    
    	// Open the process
    	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    	if (hProcess == NULL)
    		return 0;
    
    	// Allocate memory in the remote process and write a copy of EjectFunc() to the allocated memory
    	size = (PBYTE)AfterEjectFunc - (PBYTE)EjectFunc;
    	pCodeRemote = (PDWORD) VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    	if (!pCodeRemote)
    	{
    		CloseHandle(hProcess);
    		return 0;
    	}
    	if (!WriteProcessMemory(hProcess, pCodeRemote, &EjectFunc, size, &dwNumBytesCopied) || dwNumBytesCopied != size)
    	{
    		VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
    		CloseHandle(hProcess);
    		return 0;
    	}
    
    	// Start execution of the remote EjectFunc()
    	hThread = CreateRemoteThread(hProcess, 
    								 NULL, 
    								 0, 
    								 (LPTHREAD_START_ROUTINE) pCodeRemote,
    								 pDataRemote, 
    								 0 , 
    								 &dwThreadId);
    	// Failed
    	if (!hThread)
    	{
    		goto END;
    	}
    
    	// Wait for EjectFunc() to terminate and get return code
    	WaitForSingleObject(hThread, INFINITE);	
    	GetExitCodeThread(hThread, (PDWORD) &nSuccess);	
    
    	// Failed to restore old window procedure ?
    	// Then leave INJDATA and the SASWindowProc()
    	if (nSuccess == 0)		
    		goto END;			
    							
    	// Release memory for remote INJDATA and SASWindowProc()
    	if (pDataRemote)
    		VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);
    	if (pSASWinProcRemote)
    		VirtualFreeEx(hProcess, pSASWinProcRemote, 0, MEM_RELEASE);
    	pDataRemote = NULL;
    	pSASWinProcRemote = NULL;
    
    //	MessageBeep(0);		// success
    
    END:		
    	if (hThread)
    		CloseHandle(hThread);
    
    	// Release EjectFunc() memory
    	if (pCodeRemote)
    		VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
    
    	CloseHandle(hProcess);
    
    	// Disable the DEBUG privilege
    	EnablePrivilege(SE_DEBUG_NAME, FALSE);
    
    	return nSuccess;	// 0=failure; 1=success
    }

  11. #11
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    To be honest, it appears complex me. Like he said, it takes very careful coding meaning its harder. I think the method with the dll is easier, and is moe common. I think his method may require knowledge in assembly.
    Last edited by benmartin101; Apr 26th, 2007 at 04:29 PM.

  12. #12
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    If the CreateRemoteThread() is not for you, then you may want to look into SetWindowsHookEx(). There are threads here regarding this topic. It works somewhat similar to the first, in that you replace a callback function with you're own, and when this "custom" callback function is called by the external process, it will call whatever code you have placed in it. This, too, requires the use of a DLL.

  13. #13

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Hmm, well I want to try to achive this code method of doing it, because I can do it with a dll, or can at least inject it into a process.

    But what I am trying to learn is how to inject this code structure in. If you have any ideas let me know.

    I just need to figure out the right conversion for it. I am not a C++ coder, so hopfully someone can help me convert some of these lines into vb. I mean I have the concept and I can convert all the API and many functions used. But stuff dealing with the callback values in the structure or pointers and stuff are what really confuse me.

  14. #14
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    I think this is VB6 equivalent of INJDATA structure

    vb Code:
    1. public type
    2. fnSetWindowLong as long
    3. fnCllWindowProc as long
    4. fnFindWindow as long
    5. szClassName as string
    6. szWindowName as string
    7. hwnd as long
    8. fnSasWndProc as long
    9. fnOldSASWndProc as long
    10. end type

  15. #15

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Quote Originally Posted by benmartin101
    I think this is VB6 equivalent of INJDATA structure

    vb Code:
    1. public type
    2. fnSetWindowLong as long
    3. fnCllWindowProc as long
    4. fnFindWindow as long
    5. szClassName as string
    6. szWindowName as string
    7. hwnd as long
    8. fnSasWndProc as long
    9. fnOldSASWndProc as long
    10. end type

    Well yes I already had that (Shown in my above test example code) But I am trying to figure out how to set the values with the address of a function.

    Also, are those VarPtr() functions in the correct locations? I am assuming the &Variable means pass the value's pointer right?

    Do you see and flaws in the code I wrote just as a test?

  16. #16
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    this line:

    vb Code:
    1. tDataRemote.aFindWindow = GetAddressofFunction (AddressOf FindWindowA() )

    I don't think using AddressOf will work. I think AddressOf only works if the function is from your own program; FindWindowA is from a dll, user32.dll i think. You would have to use GetProcAddress() like:

    GetProcAddress("user32", "FindWindowA") would return the address of FindWindowA.

  17. #17

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Quote Originally Posted by benmartin101
    this line:

    vb Code:
    1. tDataRemote.aFindWindow = GetAddressofFunction (AddressOf FindWindowA() )

    I don't think using AddressOf will work. I think AddressOf only works if the function is from your own program; FindWindowA is from a dll, user32.dll i think. You would have to use GetProcAddress() like:

    GetProcAddress("user32", "FindWindowA") would return the address of FindWindowA.

    Ok thanks. Umm, do you know if my VarPtr() functions are in the correct locations?

  18. #18
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    They seem to be in correct locations. But the only way to really find out is by running it.

  19. #19

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Hey well Iv been trying to work with this code, but I have run into a problem

    Do you know how to covert this code to vb? :
    Code:
    /**************************************************************
     * Return the window handle of the remote process (Winlogon). *
     **************************************************************/
    
    static HWND WINAPI GetSASWnd (INJDATA *pData) 
    {
    	return (pData->fnFindWindow(pData->szClassName, pData->szWindowName));
    }
    
    
    static int AfterGetSASWnd(void) {return 4;}


    Here was my attempt, but it did not work properly:
    Code:
    '**************************************************************
    ' * Return the window handle of the remote process (Winlogon). *
    ' **************************************************************
    
    'Probelm:
    Private Function GetSASWnd() As Long
        Dim tDataLocal As InjectData
        
        '1. Attempt 1
        'GetSASWnd = FindWindow(tDataLocal.sClassName, tDataLocal.sWindowName)
        
        '2. Attempt 2
        'Dim pClassName As Long: pClassName = VarPtr(tDataLocal.sClassName)
        'Dim pWindowName As Long: pClassName = VarPtr(tDataLocal.sClassName)
        'Dim pFindWindow As Long: pFindWindow = VarPtr(tDataLocal.pFindWindow(pClassName, pWindowName))
        'GetSASWnd = pFindWindow
        
        '3. Attempt 3
        'tDataLocal.sClassName =
        
    
        
        'C++: GetSASWnd = (pData->fnFindWindow(pData->szClassName, pData->szWindowName))
    End Function
    
    Private Function AfterGetSASWnd() As Long
        AfterGetSASWnd = 4
    End Function

  20. #20
    Frenzied Member
    Join Date
    Jul 2005
    Posts
    1,168

    Re: Subclassing SAS Window

    The c++ code shows tha its using a function pointer to call the function. The question is, which function is it pointing to? If its pointing to the FindWindow() function, then your first attempt is correct.

  21. #21

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Quote Originally Posted by benmartin101
    The c++ code shows tha its using a function pointer to call the function. The question is, which function is it pointing to? If its pointing to the FindWindow() function, then your first attempt is correct.

    Well something cant be right because take a look at this:

    Code:
            Public Sub InjectCode()
    
                Dim lPID As Long
                Dim hProcess As Long
                Dim hUser32 As Long
                Dim lSize As Long
                
                Dim pDataRemote As Long
                Dim pGetSASWndRemote As Long
                Dim tDataLocal As InjectData
                Dim lNumBytesCopied As Long
                
    
                Dim sLibPath As String * 260
                Dim lLinkToLibrary As Long
                Dim NewModule As Long
    
                '--------------
    
                'Enable Debug privilege
                Call EnableProcessPrivileges(GetCurrentProcessId, SE_Debug)
    
                'Get handle of "USER32.DLL"
                hUser32 = GetModuleHandle("user32.dll")
    
                'Open Process:
                lPID = GetPID("winlogon.exe", PD_ProcessName)
                hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, lPID)
    
                'Initialize INJDATA for GetSASWnd() call
                tDataLocal.sClassName = "SAS Window class"
                tDataLocal.sWindowName = "SAS window"
                tDataLocal.pFindWindow = GetProcAddress(hUser32, "FindWindowA")
    
                'Allocate memory in the remote process and write a copy of initialized INJDATA into it
                lSize = Len(tDataLocal)     'Should = 28, and lNumBytesCopied should = 28 after WriteProcessMemory
                pDataRemote = VirtualAllocEx(hProcess, 0, lSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
                Call WriteProcessMemory(hProcess, ByVal pDataRemote, ByVal VarPtr(tDataLocal), lSize, lNumBytesCopied)
                    MsgBox lNumBytesCopied
                    lNumBytesCopied = 0
                    
                'Allocate memory in remote process and write a copy of GetSASWnd() into it
                lSize = AfterGetSASWnd - GetSASWnd
                    MsgBox lSize
                    MsgBox GetSASWnd
                pGetSASWndRemote = VirtualAllocEx(hProcess, 0, lSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
                    MsgBox pGetSASWndRemote
                Call WriteProcessMemory(hProcess, ByVal pGetSASWndRemote, ByVal VarPtr(GetSASWnd), lSize, lNumBytesCopied)
                    MsgBox lNumBytesCopied
               
            End Sub
    
    '***************************************************************
    ' * Return the window handle of the remote process (Winlogon).
    ' **************************************************************
    
    'Problem:
    Private Function GetSASWnd() As Long
        Dim tDataLocal As InjectData
        
        '1. Attempt 1
        GetSASWnd = FindWindow(tDataLocal.sClassName, tDataLocal.sWindowName)
        
        '2. Attempt 2
        'Dim pClassName As Long: pClassName = VarPtr(tDataLocal.sClassName)
        'Dim pWindowName As Long: pClassName = VarPtr(tDataLocal.sClassName)
        'Dim pFindWindow As Long: pFindWindow = VarPtr(tDataLocal.pFindWindow(pClassName, pWindowName))
        'GetSASWnd = pFindWindow
        
        '3. Attempt 3
        'tDataLocal.sClassName =
        
    
        
        'C++: GetSASWnd = (pData->fnFindWindow(pData->szClassName, pData->szWindowName))
    End Function
    
    Private Function AfterGetSASWnd() As Long
        AfterGetSASWnd = 4
    End Function
    Here is why something is wrong:
    lNumBytesCopied retruns 0
    The reason why is because pGetSASWndRemote returns 0.....
    meaning the size allocation is not correct.

    lSize depends on those 2 functions where I am unsure about, so it obviously has seomthing to do this that. Then reason why I dont think method 1 is correct because it is like saying:

    MemoryAllocationSize = 4 - WindowHandle

    Which doesn't make sence.


    So in conclusion, everthing works fine until I get up to the:
    lSize = AfterGetSASWnd - GetSASWnd

    So.... please help.

    NOTE: it might be easier to help if you just slap in the above code into vb since I have many comments to help me along the way.
    Last edited by altf4; Apr 28th, 2007 at 02:06 PM.

  22. #22

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Any ideas anyone?

    I am kind of dead in the water here.

  23. #23

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    *Bump*

  24. #24

    Thread Starter
    Lively Member
    Join Date
    May 2004
    Location
    Home
    Posts
    85

    Re: Subclassing SAS Window

    Well after a year and a half of work in VB since this post asking for help, I finally figured it out!

    Its funny, I don't really have a purpose for doing it anymore since I have found many better ways to protect a process (Such as removing the process from the list in the Kernel) but it was an interesting journey.

    I was way over my head before, and not understanding about memory management and threads was a big mistake before trying to tackle this problem.

    Anyway, just thought I would let everyone know that it is possible in VB6 without needing a C DLL. The trick is: making it with a VB active-x Dll with some mods (Thanks to Jim White for all his research and help how to successfully compile a win32 dll and call APIs using a TypeLibrary (generated from a .old)

    Here is the thread for which helped out a lot with the DLL trouble shooting:
    http://www.xtremevbtalk.com/showthread.php?p=1312981

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width