Jump to content

Start a process hidden


Go to solution Solved by Decipher,

Recommended Posts

I know of two examples which should sufficently explain what I wish to accomplish. The first is when using SSH, a remote session can start any process but that process will not have a GUI maybe because it lacks permissions. The second is when an application is configured to run as a service, there is an option to allow the service to interact with the desktop. So I want to start a process which dosen't have permission to interact with the desktop(hidden with no GUI creation capabillity) where any child processes are also hidden because they inherit from their parent process. @SW_HIDE isn't an option here as it dosen't restrict the process, it only hides the specified window which can be overriden and any process started by it will not have been hidden. I do not want to create a service.

Why? Take for example the portable version of putty, in the PortableApps.com(PA) format. I can launch PuttyPortable.exe with the @SW_HIDE flag but Putty's GUI(putty.exe) will still show because it is a new window created by a different(child) process.

A lame solution:

1. Get the list of child processes for PuttyPortable.exe

2. Check all the opened window's processes against the child process

3. If there is match then hide the window using the handle which was consistent.

4. Use all of the above in a tight loop

The window will still show momentarily which is an annoyance. So how can I start a process hidden besides the already mentioned methods?

I've tried searching but I don't understand exactly how the examples I mention are accomplishing this or what its called. I thought perhaps their spawning the process with a different desktop parameter, i.e. Shown on a nonexistent desktop.

Thanks in advance,

Anonymous

Spoiler

censored.jpg

 

Link to comment
Share on other sites

0xdefea7,

Thanks for the direction. I gave it my best shot but I keep getting a error from windows: The application was unable to start correctly (0xc0000142). Click OK to close the application. The process does exist and from what I can tell everything executes properly except when I attempt to set the desktop thread which I'm not sure I need in the first place.

#include <WinAPI.au3>
#include <WinAPIEx.au3>

ConsoleWrite("!>Return: " & _RunProcessHidden(@ComSpec) & @CRLF & "!>@error: " & @error & @CRLF & "!>@extended: " & @CRLF)

Func _RunProcessHidden($sProcessName, $sParam = 0)
    ; Prep
    Local $tStartupInfo = DllStructCreate($tagStartupInfo)
    DllStructSetData($tStartupInfo, "Size", DllStructGetSize($tStartupInfo))
    Local $tProcessInfo = DllStructCreate($tagPROCESS_INFORMATION)
    ; Create Desktop
    Local $aRet = DllCall("User32.dll", "handle", "CreateDesktop", "str", "AutoItHidden", "str", 0, "ptr", 0, "dword", 0, "dword", $GENERIC_ALL, "dword", 0)
    If Not $aRet[0] Then Return SetError(1, 0, "")
    ; Prep Process Info
    Local $nSuccess = DllStructSetData($tStartupInfo, "Desktop", _WinAPI_CreateString("AutoItHidden"))
    If Not $nSuccess Then Return SetError(2, DllStructGetData($tStartupInfo, "Desktop"), "")
    ; Set Desktop Thread
    Local $aThread = DllCall("User32.dll", "handle", "GetThreadDesktop", "dword", _WinAPI_GetCurrentThreadId())
    $aThread = DllCall("User32.dll", "int", "SetThreadDesktop", "handle", $aThread[0])
    ConsoleWrite("!>Set Thread: " & $aThread[0] & @CRLF) ; Non-Zero is successfull!
    ; Create Process
    _WinAPI_CreateProcess($sProcessName, $sParam, 0, 0, True, 0, 0, 0, DllStructGetPtr($tStartupInfo), DllStructGetPtr($tProcessInfo))
    If @error Then Return SetError(3, _WinAPI_GetLastError(), "")
    ; Cleanup and return PID
    $aRet = DllCall("User32.dll", "int", "CloseDesktop", "handle", $aRet[0])
    ConsoleWrite("!>Close Desktop: " & $aRet[0] & @CRLF) ; Non-Zero is successfull!
    Local $aPID = DllCall("Kernel32.dll", "dword", "GetProcessId", "handle", DllStructGetData($tProcessInfo, "hProcess"))
    ;Sleep(2000)
    ;ProcessClose($aPID)
    Return $aPID[0]
EndFunc

Anonymous

Spoiler

censored.jpg

 

Link to comment
Share on other sites

The only thing I can see wrong is that you are closing the desktop right after opening it. You can't run a process is the context of a session that does not exist :) That said, the code in my post works as expected when I comment out the ChangeDesktop functions. 

To explain a bit further, when you close a desktop, all it's processes come along with it. It looks like that is all the error was because I commented that out and now it works as expected.

Edited by 0xdefea7
Link to comment
Share on other sites

oxdefea7,

Thanks for your help with this but I think you posted the wrong link. I did however find your code and research, wasn't hard. ;)

As of right now, closing the desktop handle is currently just pusedo-code. I thought that if it did work than maybe it would prevent conflicts with other multi-destop featured applications(I mean creating the window on an already existence desktop with the posibility of that desktop being in view). The error I mentioned above still occurs despite the obvious...I'm still hoping to find a solution to this!

Update:

I just tested the code I posted above as is on XP HOME SP3(32bit du) and it works. Beside success, the only difference is that the SetDesktopThread function returned zero(successful) and the CloseDesktop call does return non-zero as you suggested oddly it dosen't on Windows 7. I have tried without closing the desktop repeatedly.

The error I was recieving before was on Windows 7 Professional 64bit with administrative privileges(natively - UAC disabled).

So since this is for use with portable applications, I'm still at a loss....

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

I don;t know what other solution you need, do what I mentioned in my post and it will do exactly what you want. You will not find another reliable way to do what you want. 

 

I thought that if it did work than maybe it would prevent conflicts with other multi-destop featured applications(I mean creating the window on an already existence desktop with the posibility of that desktop being in view). The error I mentioned above still occurs despite the obvious...but I understand the need to rule out such flaws as they could become hard to determine problems in large projects.

 

I don't understand what you are saying. There is no risk of this clashing with other applications. When you create a desktop, it is your specific desktop. If you were, for example, to use Sysinternals Desktops, there is no possibility (unless you happened to name your desktops the same as they did or enum handles and grab thiers by accident) that this could clash with any other app or cause adverse effects. Good luck in your search.

This opens calc.exe (check procexp ot taskmgr while running)

#include <WinAPI.au3>
#include <WinAPIEx.au3>

ConsoleWrite("!>Return: " & _RunProcessHidden("C:\windows\system32\calc.exe") & @CRLF & "!>@error: " & @error & @CRLF & "!>@extended: " & @CRLF)

Func _RunProcessHidden($sProcessName, $sParam = 0)
    ; Prep
    Local $tStartupInfo = DllStructCreate($tagStartupInfo)
    DllStructSetData($tStartupInfo, "Size", DllStructGetSize($tStartupInfo))
    Local $tProcessInfo = DllStructCreate($tagPROCESS_INFORMATION)
    ; Create Desktop
    Local $aRet = DllCall("User32.dll", "handle", "CreateDesktop", "str", "AutoItHidden", "str", 0, "ptr", 0, "dword", 0, "dword", $GENERIC_ALL, "dword", 0)
    If Not $aRet[0] Then Return SetError(1, 0, "")
    ; Prep Process Info
    Local $nSuccess = DllStructSetData($tStartupInfo, "Desktop", _WinAPI_CreateString("AutoItHidden"))
    If Not $nSuccess Then Return SetError(2, DllStructGetData($tStartupInfo, "Desktop"), "")
    ; Set Desktop Thread
    Local $aThread = DllCall("User32.dll", "handle", "GetThreadDesktop", "dword", _WinAPI_GetCurrentThreadId())
    $aThread = DllCall("User32.dll", "int", "SetThreadDesktop", "handle", $aThread[0])
    ConsoleWrite("!>Set Thread: " & $aThread[0] & @CRLF) ; Non-Zero is successfull!
    ; Create Process
    _WinAPI_CreateProcess($sProcessName, $sParam, 0, 0, True, 0, 0, 0, DllStructGetPtr($tStartupInfo), DllStructGetPtr($tProcessInfo))
    If @error Then Return SetError(3, _WinAPI_GetLastError(), "")
    ; Cleanup and return PID
;~     $aRet = DllCall("User32.dll", "int", "CloseDesktop", "handle", $aRet[0])
    ConsoleWrite("!>Close Desktop: " & $aRet[0] & @CRLF) ; Non-Zero is successfull!
    Local $aPID = DllCall("Kernel32.dll", "dword", "GetProcessId", "handle", DllStructGetData($tProcessInfo, "hProcess"))
    ;Sleep(2000)
    ;ProcessClose($aPID)
    Return $aPID[0]
EndFunc

It is the same code you posted, with the one line I told you to comment out, commented out. Tested on XP, and 7. If this does not work for you, you are doing something else wrong.

Edited by 0xdefea7
Link to comment
Share on other sites

oxdefea7,

I understand now that I don't have to worry about the desktop's creation causing conflict because it should have its own unique handle and only my application should know that handle. However I don't understand why I'm getting the error. It might be the system's current configuration but it is stock(My work PC) or pherhaps the issue is with the portable editon of AutoIt and/or Scite. Dunno. What was the return value of the SetDesktopThread function?

Edit:

The problem obviously lies with the process creation and there are two dll structs that are created for this purpose. I wonder if running the AutoIt3Wrapper is 64bit mode is causing the problem. The two constants that define the size of the structures, are they compatible with both 32bit and 64bit?? If there is no other conclusion to draw out then I will test it tomorrow at work.

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

0xdefea7,

!>

+>

>

-->

Thats all I know ;)

I don't know if this is revelant but I stumbled upon the CreateWindowStation function on MSDN and was wondering if you could elaborate its use.

Edit - Spelling.

Thanks for your help thus far. :)

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

This same thing works on all OS and architecture. I wrote and tested on win 7 x64 and then tested it on XP after. It doesn't matter if you run as admin or not, this script does not require admin. You should try things in this order when you want to test on another machine:

1. Run the code and test on the working machine - make sure it works as is

2. Compile - compile for x86 and UpX off

3. Run on the test machine

Think of CreateWindowStation as one level up from the functions we are working with now. So each Desktop space resides within a Window Station. By default, we are all in station0 and we can create a new station if we would like. I have honestly had no reason to use it, since I can create new desktops within station0.

Link to comment
Share on other sites

  • Solution

Apparently there is a more direct way to do this using WinAPI. It revolves around Window's stations. If you set the lpDesktop member of the StartupInfo struct to an empty string which is different from a null(unintialized value) then windows will create a WindowStation which doesn't interact with the desktop by default. However its theroretical. I attempted this but I have little knowledge of DLL structs and DLL calls but I can share my research. ;)

Read the green people!

__________________________________________________________________________________________________

http://msdn.microsoft.com/en-us/library/ms684859%28v=vs.85%29.aspx

  • If a window station name was specified in the lpDesktop member of the STARTUPINFO structure that was used when the process was created, the process connects to the specified window station.
  • Otherwise, if the process is running in the logon session of the interactive user, the process connects to the interactive window station.
  • If the open operation fails because this window station does not exist, the system tries to create the window station and a default desktop.

 

If you omit the "Desktop" value for the struct then Windows should connect the process to the default station and desktop object which is "winsta0default" unless the calling process isn't using the default station and desktop, because of inheritance.

http://msdn.microsoft.com/en-us/library/ms687105%28v=vs.85%29.aspx

  • When a noninteractive process such as a service application attempts to connect to a window station and no window station exists for the process logon session, the system attempts to create a window station and desktop for the session.

Translation: When a process attempts to connect to a non-existent window station the system attempts to create one for you!

  • If a service is running in the security context of the LocalSystem account but does not include the SERVICE_INTERACTIVE_PROCESS attribute, it uses the following window station and desktop: Service-0x0-3e7$default. This window station is not interactive, so the service cannot display a user interface. In addition, processes created by the service cannot display a user interface.

I'm not so concerned with the security context or naming convention as I wouldn't have the name of the station at process creation time, you can't specify a name without admin rights that I know of. My interest is that it suggests that the dwflag SERVICE_INTERACTIVE_PROCESS is used at process creation time by services that are user interactive. It suggests that the system doesn't create interactive window stations by default.

__________________________________________________________________________________________________

So I think its safe to say that this code will do just as good as the previous.

#include <WinAPI.au3>
#include <WinAPIEx.au3>

ConsoleWrite("!>Return: " & _RunProcessHidden(@SystemDir & '\calc.exe') & @CRLF & "!>@error: " & @error & @CRLF & "!>@extended: " & @CRLF)
Func _RunProcessHidden($sProcessName, $sParam = 0)
    Local $tStartupInfo = DllStructCreate($tagStartupInfo)
    DllStructSetData($tStartupInfo, "Size", DllStructGetSize($tStartupInfo))
    Local $tProcessInfo = DllStructCreate($tagPROCESS_INFORMATION)
    If Not DllStructSetData($tStartupInfo, "Desktop", _WinAPI_CreateString("AutoItHidden")) Then Return SetError(2, @error, "")
    _WinAPI_CreateProcess($sProcessName, $sParam, 0, 0, True, 0, 0, 0, DllStructGetPtr($tStartupInfo), DllStructGetPtr($tProcessInfo))
    If @error Then Return SetError(3, _WinAPI_GetLastError(), "")
    Local $aPID = DllCall("Kernel32.dll", "dword", "GetProcessId", "handle", DllStructGetData($tProcessInfo, "hProcess"))
    Return $aPID[0]
EndFunc

It seems that the error that I get only happens when executing PortablePutty.exe using this code..............Help?

Edit - I've confirmed that this works using the following compiled script on Windows 7 Professional 64bit.

GUISetState(@SW_SHOW, GUICreate("Test"))
For $i = 0 To 99
    FileWriteLine(@DesktopDir & "\Test.txt", "Test" & @CRLF)
    Sleep(50)
Next
Exit

No system tray icon, etc.

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

JohnOne,

Lol, I was wondering when you would pop up. It hides any process and any child processes of that process from the user's interface. It should successfully prevent your standard application from creating a system tray icon, GUI, or anything else visible on the user's desktop from the moment the process is instantiated. ;)

Edit - Beware it doesn't prevent JohnOne.exe from posting on the forum. :P

Anonymous

Edited by Decipher
Spoiler

censored.jpg

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...