Protected frontend GUI

BlitzMax Forums/BlitzMax Programming/Protected frontend GUI

(EDIT - Maybe this should be in GUI programming??)


I am starting a project whereby I want to create a protected frontend GUI which restricts a user from tampering with the Windows installation.

For me, its far too easy for a casual user to accidently screw around with the setup. Renaming, deleting, moving icons, deleting stuff from folders, messing up the display, installing spyware freebie stuff, etc..

I really want to crack down hard and set major limitations in place.

My idea is to create a protected frontend display much like you see in a Kiosk. Something which still lets the user click icons, open utilities, and the likes but prevents them from tampering with the O/S itself. Basically, a sandboxed system.

So, I need to:

1) Stop the user Alt-Tabbing into the O/S
2) Kill all system keys such as Ctrl+Alt,Del, the Windows key, menu key, sticky keys.
3) Kill all popup alerts ("Updates ready", "Virus database out of date")
4) Allow only selected drives/folders to be viewable from applications like Notepad, MSPaint
5) Hide the windows taskbar (if 'Always on top' is selected)


Just to give you a taster here is a 'very basic' frontend GUI
SuperStrict

Framework BRL.Win32MaxGUI

AppTitle="ProtectedGUI"

Global dt:TGadget=Desktop()
Global dtW%=dt.Width
Global dtH%=dt.Height

Global taskbarH%=52
Global panW%=140

Global time$=CurrentTime$()
Global mb%

Global win:TGadget=CreateWindow("Test",0,-18,dtW,dtH+18,Desktop(),EVENT_WINDOWACTIVATE)
Global displaypanel:TGadget=CreatePanel(0,0,win.Width,win.Height-taskbarH,win,PANEL_ACTIVE)

Global taskbarpanel:TGadget=CreatePanel(0,displaypanel.Height,win.Width,taskbarH-18,win,PANEL_ACTIVE)
Global btn_Menu:TGadget=CreateButton("Menu",4,4,56,taskbarpanel.Height-8,taskbarpanel)
Global taskbarinfo:TGadget=CreatePanel(win.Width-panW-4,4,panW,taskbarpanel.Height-8,taskbarpanel,PANEL_BORDER)
Global lbl_Time:TGadget=CreateLabel("",taskbarinfo.Width-38,4,38,taskbarinfo.Height-8,taskbarinfo)

SetPanelColor displaypanel,$22,$44,$aa
SetGadgetText lbl_Time,time[0..time.Length-3]


Global popupmenu:TGadget=CreateMenu(AppTitle,000,Null)
CreateMenu("Preferences",101,popupmenu)
CreateMenu("",000,popupmenu)
CreateMenu("Switch User",102,popupmenu)
CreateMenu("Exit",103,popupmenu)

Global debugwindow:TGadget=CreateListBox(10,10,400,142,displaypanel)

Repeat
	Local s$
	'Debug CurrentEvent.ToString()
	Select WaitEvent()
		Case EVENT_MOUSEMOVE
		Case EVENT_MOUSEDOWN
		mb=EventData()
		If EventSource()=displaypanel s$="Display"
		If EventSource()=taskbarpanel s$="TaskBar"
		Debug s$+" > Mouse Button  = "+String(mb)
		Case EVENT_GADGETACTION
			Select EventSource()
				Case btn_Menu
				MoveMouse taskbarpanel.XPos+4 , taskbarpanel.YPos
				PopupWindowMenu win,popupmenu
			End Select
		Case EVENT_MENUACTION
		Debug "Menu > "+EventData()
		If EventData()=103 Exit
	End Select
Forever
End


Function Debug(txt$)
	If CountGadgetItems(debugwindow)>8 ClearGadgetItems debugwindow
	If txt$<>""
		AddGadgetItem debugwindow,txt$
		SelectGadgetItem debugwindow,CountGadgetItems(debugwindow)-1
	EndIf
End Function


My idea is to add a "Admin" mode which allows the complete control of what is accessible to the user, as well as a quit option to return back to Windows.

Task too big?

Didn't do any hook programming on Windows yet... but I heard that's a lot of work.. :|

So is this like a desktop environment?

You could use multiple desktops. Windows creates the most gui stuff on the Default desktop. So if you create another desktop and run your application on this desktop you could have a much more secure system than normal windows. Using a seperate desktop can solve all points in the list you made except number 4.

I wrote some code showing how this could work, if the app is started once the user can't switch back to the Default desktop manually. You need to click the application's button to close it and return to windows - however, you can easily change the application's source code to make the button invisible or disabled and then the user can't get back to the OS at all. I just tried out myself - even Ctrl-Alt-Del doesn't help. So here's the code:
Strict
Rem
bbdoc:WinDesktop
EndRem
'Module fmc.WinDesktop

'ModuleInfo "Version: 0.06"
'ModuleInfo "Modserver: Fabian"
?Win32

Import brl.linkedlist
Import brl.win32maxgui

Const LabelText$ = "Be sure that the user has no possibility to switch back the desktop manually; "+..
"only by clicking the button below you can access to the old windows system desktop, however, "+..
"you can easily edit this code and write it without the button."

If Len AppArgs = 2
  Global Window:TGadget = CreateWindow ( "" , 50 , 50 , 400 , 424 , Desktop ( ) , 0 )
  Global Label:TGadget = CreateLabel ( LabelText , 0 , 0 , 400 , 400 , Window )
  Global Button:TGadget = CreateButton ( "Switch back, please!" , 0 , 400 , 400 , 24 , Window )
  AddHook EmitEventHook , Func
  Global Ending
  While Not Ending
    WaitEvent
  Wend

  Function Func:Object ( id , data:Object , context:Object )
    Local Event:TEvent = TEvent ( data )
    If Event.source = Button And Event.id = EVENT_GADGETACTION
      SwitchUserDesktop AppArgs [ 1 ]
      Ending = True
    EndIf
    Return data
  EndFunction
Else
  Local Desk$ = "OurNewDesktop"
  CreateProcessOnDesktop "~q" + AppFile + "~q Default" , Desk
  While Not SwitchUserDesktop ( Desk )
  Wend
EndIf

Rem
bbdoc:Create a process on a specified desktop
returns:#brl.blitz.True if successfully, else #brl.blitz.False
EndRem
Function CreateProcessOnDesktop ( CmdLine$ , Desk$ )
  Local Success
  Local StartupInfo:TStartupInfo = New TStartupInfo
  Local ProcessInformation:TProcessInformation = New TProcessInformation
  StartupInfo.Size = SizeOf TStartupInfo
  StartupInfo.Desk = Desk.ToWString ( )
  Local Cmd:Short Ptr = CmdLine.ToWString ( )
  If CreateProcessW ( Null , Cmd , Null , Null , False , 0 , Null , Null , StartupInfo , ProcessInformation )
    CloseHandle ProcessInformation.Process
    CloseHandle ProcessInformation.Thread
    Success = True
  EndIf
  MemFree StartupInfo.Desk
  MemFree Cmd
  Return Success
EndFunction

Rem
bbdoc:Change the user's desktop
returns:#brl.blitz.True if successfully, else #brl.blitz.False
EndRem
Function SwitchUserDesktop ( Name$ )
  Local Success
  Local Desk:Short Ptr = Name.ToWString ( )
  Local Handle = OpenDesktopW ( Desk , 0 , False , $100 )
  If Handle
    If SwitchDesktop ( Handle )
      Success = True
    EndIf
    CloseDesktop Handle
  EndIf
  MemFree Desk
  Return Success
EndFunction

Rem
bbdoc:Get the desktop names
returns:A string array containing the names of the desktops
EndRem
Function GetDesktops$ [] ( )
  Local List:TList = CreateList ( )
  EnumDesktopsW GetProcessWindowStation ( ) , EnumProc , ( Int Ptr Varptr List ) [ 0 ]
  Local ObjectArray:Object [] = ListToArray ( List )
  Local StringArray$ [ Len ObjectArray ]
  For Local I = 0 Until Len StringArray
    StringArray [ I ] = String ObjectArray [ I ]
  Next
  Return StringArray

  Function EnumProc ( Desk:Short Ptr , Param:TList )
    ListAddLast Param , String.FromWString ( Desk )
    Return True
  EndFunction
EndFunction

Private

Type TProcessInformation
  Field Process
  Field Thread
  Field ProcessId
  Field ThreadId
EndType

Type TStartupInfo
  Field Size
  Field Reserved0:Short Ptr
  Field Desk:Short Ptr
  Field Title:Short Ptr
  Field X
  Field Y
  Field XSize
  Field YSize
  Field XCountChars
  Field YCountChars
  Field FillAttribute
  Field Flags
  Field ShowWindow:Short
  Field Reserved1:Short
  Field Reserved2:Byte Ptr
  Field StdInput
  Field StdOutput
  Field StdError
EndType

Extern "Win32"
  Function EnumDesktopsW ( WinSta , Func:Byte Ptr , Param )
  Function GetProcessWindowStation ( )
  Function CreateProcessW ( App:Short Ptr , Cmd:Short Ptr , PA:Byte Ptr , TA:Byte Ptr , IH , CF , Env:Byte Ptr , CD:Short Ptr , SI:Byte Ptr , P:Byte Ptr )
  Function CloseHandle ( Obj )
  Function OpenDesktopW ( Name:Short Ptr , F , I , DA )
  Function CloseDesktop ( Desk )
  Function SwitchDesktop ( Desk )
EndExtern
?

The functions are extracted from my module, feel free to use them in your applications if you like.
I used code like this to create a winlock tool (source here), which can be used to protect the system if you are temorary away and leave the system alone. The application asks you for a password and as soon as you entered it the OS gets locked and you can't do anything if you don't enter the password again.

I'm quite sure that the user can't do anything on the secondary desktop; except the actions you explicitly allowed.

Great work Franz.
Thats the sort of thing I'm after.
I'll have a play. Many thanks!