computer-use-ootb-internal 0.0.162__tar.gz → 0.0.164__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/PKG-INFO +1 -1
  2. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/pyproject.toml +1 -1
  3. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/guard_service.py +164 -76
  4. computer_use_ootb_internal-0.0.164/src/computer_use_ootb_internal/launch_ootb_elevated.ps1 +69 -0
  5. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/.gitignore +0 -0
  6. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/README.md +0 -0
  7. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/__init__.py +0 -0
  8. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/app_teachmode.py +0 -0
  9. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/animation/click_animation.py +0 -0
  10. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/animation/icons8-select-cursor-transparent-96.gif +0 -0
  11. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/animation/test_animation.py +0 -0
  12. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/executor/teachmode_executor.py +0 -0
  13. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/__init__.py +0 -0
  14. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/gui_capture.py +0 -0
  15. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/simple_parser/utils.py +0 -0
  16. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/__init__.py +0 -0
  17. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/screenshot_cli.py +0 -0
  18. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/gui_parser/uia_tools/screenshot_service.py +0 -0
  19. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/llm_utils.py +0 -0
  20. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/oai.py +0 -0
  21. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/run_litellm.py +0 -0
  22. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/gui_agent/llm_utils/run_llm.py +0 -0
  23. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/__init__.py +0 -0
  24. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/aws_request.py +0 -0
  25. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/base.py +0 -0
  26. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/bash.py +0 -0
  27. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/collection.py +0 -0
  28. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/colorful_text.py +0 -0
  29. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/computer.py +0 -0
  30. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/computer_marbot.py +0 -0
  31. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/edit.py +0 -0
  32. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/run.py +0 -0
  33. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/computer_use_demo/tools/screen_capture.py +0 -0
  34. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/dependency_check.py +0 -0
  35. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/preparation/__init__.py +0 -0
  36. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/preparation/star_rail_prepare.py +0 -0
  37. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/requirements-lite.txt +0 -0
  38. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/run_teachmode_ootb_args.py +0 -0
  39. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/service_manager.py +0 -0
  40. {computer_use_ootb_internal-0.0.162 → computer_use_ootb_internal-0.0.164}/src/computer_use_ootb_internal/signal_connection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: computer-use-ootb-internal
3
- Version: 0.0.162
3
+ Version: 0.0.164
4
4
  Summary: Computer Use OOTB
5
5
  Author-email: Siyuan Hu <siyuan.hu.sg@gmail.com>
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "computer-use-ootb-internal"
7
- version = "0.0.162"
7
+ version = "0.0.164"
8
8
  description = "Computer Use OOTB"
9
9
  authors = [{ name = "Siyuan Hu", email = "siyuan.hu.sg@gmail.com" }]
10
10
  requires-python = ">=3.11"
@@ -23,6 +23,7 @@ import psutil # For process/user info
23
23
  from flask import Flask, request, jsonify # For embedded server
24
24
  from waitress import serve # For serving Flask app
25
25
  import json # Needed for status reporting
26
+ import shutil # Added for shutil.which
26
27
 
27
28
  # --- Configuration ---
28
29
  _SERVICE_NAME = "OOTBGuardService"
@@ -226,7 +227,9 @@ class GuardService(win32serviceutil.ServiceFramework):
226
227
  self.log_info(f"Using target executable: {self.target_executable_path}")
227
228
 
228
229
  _service_instance = self
229
- self.log_info(f"Service initialized. Target executable: {self.target_executable_path}.")
230
+ # Determine path to the signal script
231
+ self.signal_script_path = self._find_signal_script()
232
+ self.log_info(f"Service initialized. Target executable: {self.target_executable_path}. Signal script: {self.signal_script_path}")
230
233
 
231
234
  def SvcStop(self):
232
235
  self.log_info(f"Service stop requested.")
@@ -706,19 +709,9 @@ class GuardService(win32serviceutil.ServiceFramework):
706
709
 
707
710
 
708
711
  try:
709
- # 1. Ensure scheduled task exists (still useful fallback/persistence)
710
- try:
711
- task_created = self.create_or_update_logon_task(user, calculated_port)
712
- task_created_status = "task_success" if task_created else "task_failed"
713
- except Exception as task_err:
714
- self.log_error(f"Internal trigger: Exception creating/updating task for {user}: {task_err}", exc_info=True)
715
- task_created_status = "task_exception"
716
- # Don't necessarily fail the whole operation yet
717
-
718
712
  # 2. Check if user is active
719
713
  active_sessions = {} # Re-check active sessions specifically for this user
720
714
  session_id = None
721
- token = None
722
715
  is_active = False
723
716
  try:
724
717
  sessions = win32ts.WTSEnumerateSessions(win32ts.WTS_CURRENT_SERVER_HANDLE)
@@ -739,7 +732,7 @@ class GuardService(win32serviceutil.ServiceFramework):
739
732
  if not is_active:
740
733
  self.log_info(f"Internal trigger: User '{user}' is not active. Skipping immediate start.")
741
734
  immediate_start_status = "start_skipped_inactive"
742
- final_status = task_created_status # Status depends only on task creation
735
+ final_status = "skipped_inactive" # Simplified status
743
736
  return final_status # Exit early if inactive
744
737
 
745
738
  # 3. Check if already running for this active user
@@ -756,61 +749,100 @@ class GuardService(win32serviceutil.ServiceFramework):
756
749
  self.log_error(f"Internal trigger: Error checking existing processes for {user}: {e}")
757
750
  # Continue and attempt start despite error?
758
751
 
759
- # 4. Create/Update and Trigger Scheduled Task
760
- immediate_start_status = "start_attempted_task_trigger"
761
- self.log_info(f"Internal trigger: User '{user}' is active and not running. Attempting start via Task Scheduler...")
752
+ # 4. Attempt immediate start using CreateProcessAsUser to run helper script
753
+ immediate_start_status = "start_attempted_helper_script"
754
+ self.log_info(f"Internal trigger: User '{user}' is active and not running. Attempting start via helper script...")
755
+
756
+ if not self.target_executable_path:
757
+ self.log_error("Internal trigger: Cannot start process - target executable path not found.")
758
+ final_status = "failed_exe_not_found"
759
+ return final_status
762
760
 
763
- # --- Start Task Scheduler logic ---
764
761
  try:
765
- # Step 4a: Ensure the task is created/updated correctly
766
- # Pass calculated port to the task creation function
767
- task_created = self.create_or_update_logon_task(user, calculated_port)
768
- if not task_created:
769
- self.log_error(f"Internal trigger: Failed to create/update scheduled task for user '{user}'. Cannot proceed with start.")
770
- final_status = "failed_task_creation"
762
+ # Find helper script path
763
+ helper_script_path = self._find_helper_script("launch_ootb_elevated.ps1")
764
+ if not helper_script_path:
765
+ self.log_error("Internal trigger: Cannot start process - helper script not found.")
766
+ final_status = "failed_helper_script_not_found"
771
767
  return final_status
772
-
773
- # Step 4b: Trigger the task to run immediately
774
- task_name = f"OOTB_UserConnect_{user}" # Must match the name used in create_or_update_logon_task
775
- trigger_command = ["schtasks", "/Run", "/TN", task_name]
776
- self.log_info(f"Internal trigger: Attempting to run task '{task_name}' immediately using: {trigger_command}")
777
-
778
- result = subprocess.run(
779
- trigger_command,
780
- capture_output=True,
781
- text=True,
782
- check=False, # Check return code manually
783
- encoding='utf-8',
784
- errors='ignore'
768
+
769
+ # Get PowerShell executable path
770
+ powershell_executable = self._find_powershell_executable()
771
+ if not powershell_executable:
772
+ self.log_error("Internal trigger: Cannot start process - powershell.exe not found.")
773
+ final_status = "failed_powershell_not_found"
774
+ return final_status
775
+
776
+ # Prepare arguments for the helper script (paths need quoting if they contain spaces)
777
+ target_exe_unquoted = self.target_executable_path.strip('"')
778
+ target_exe_dir = os.path.dirname(target_exe_unquoted) # Use directory of unquoted path
779
+
780
+ # Quote paths/args for command line - IMPORTANT: Use double quotes for PowerShell args
781
+ helper_script_arg = f'"{helper_script_path}"' if " " in helper_script_path else helper_script_path
782
+ target_exe_arg = f'"{target_exe_unquoted}"' if " " in target_exe_unquoted else target_exe_unquoted
783
+ working_dir_arg = f'"{target_exe_dir}"' if " " in target_exe_dir else target_exe_dir
784
+ # Username might need quoting if it contains special characters, double quote should be safe
785
+ target_user_arg = f'"{user}"'
786
+
787
+ # Construct the command line to execute powershell with the helper script and args
788
+ lpCommandLine = (
789
+ f'"{powershell_executable}" -NoProfile -ExecutionPolicy Bypass -NoExit '
790
+ f'-File {helper_script_arg} '
791
+ f'-TargetExePath {target_exe_arg} '
792
+ f'-Port {calculated_port} '
793
+ f'-TargetUser {target_user_arg} '
794
+ f'-WorkingDirectory {working_dir_arg}'
785
795
  )
786
796
 
787
- if result.stdout:
788
- self.log_info(f"Internal trigger: schtasks /Run STDOUT:\n{result.stdout.strip()}")
789
- if result.stderr:
790
- # schtasks often prints success message to stderr, log as info
791
- self.log_info(f"Internal trigger: schtasks /Run STDERR:\n{result.stderr.strip()}")
792
-
793
- if result.returncode == 0 or "SUCCESS: Attempted to run the scheduled task" in result.stdout or "SUCCESS: Attempted to run the scheduled task" in result.stderr:
794
- self.log_info(f"Internal trigger: Successfully triggered task '{task_name}' for user '{user}'.")
795
- immediate_start_status = "start_success_task_triggered"
796
- final_status = "success_task_triggered"
797
- else:
798
- self.log_error(f"Internal trigger: Failed to trigger task '{task_name}' (Exit Code: {result.returncode}).")
799
- immediate_start_status = f"start_failed_task_trigger_code_{result.returncode}"
800
- final_status = f"failed_task_trigger_code_{result.returncode}"
801
-
802
- except FileNotFoundError:
803
- self.log_error("Internal trigger: 'schtasks.exe' not found.")
804
- immediate_start_status = "start_failed_schtasks_not_found"
805
- final_status = "failed_schtasks_not_found"
806
- except Exception as task_trigger_err:
807
- self.log_error(f"Internal trigger: Exception during task trigger for user '{user}': {task_trigger_err}", exc_info=True)
808
- immediate_start_status = "start_failed_task_trigger_exception"
809
- final_status = "failed_task_trigger_exception"
810
- # --- End Task Scheduler logic ---
797
+ # Use CreateProcessAsUser to launch this command in the user's session
798
+ token = win32ts.WTSQueryUserToken(session_id)
799
+ env = win32profile.CreateEnvironmentBlock(token, False)
800
+ startup = win32process.STARTUPINFO()
801
+ # Ensure the window is visible on the user's desktop
802
+ startup.lpDesktop = "winsta0\\default"
803
+ creation_flags = win32con.CREATE_NEW_CONSOLE | win32con.NORMAL_PRIORITY_CLASS
804
+ lpApplicationName = None # Must be None when using lpCommandLine
805
+ # Working directory for powershell itself? Let it default or use script dir?
806
+ # Let's use the script's directory as CWD for powershell.exe
807
+ cwd = os.path.dirname(helper_script_path) # Use directory of unquoted path
808
+
809
+ self.log_info(f"Internal trigger: Launching helper script via CreateProcessAsUser:")
810
+ self.log_info(f" lpCommandLine: {lpCommandLine}")
811
+ self.log_info(f" lpCurrentDirectory: {cwd}")
812
+
813
+ # Execute the command in the user's session
814
+ hProcess, hThread, dwPid, dwTid = win32process.CreateProcessAsUser(
815
+ token, lpApplicationName, lpCommandLine, None, None, False,
816
+ creation_flags, env, cwd, startup
817
+ )
818
+ self.log_info(f"Internal trigger: CreateProcessAsUser call for helper script succeeded (Launcher PID: {dwPid}). Helper script should now run and trigger elevation.")
819
+ win32api.CloseHandle(hProcess)
820
+ win32api.CloseHandle(hThread)
821
+
822
+ # Assume success if CreateProcessAsUser succeeded.
823
+ # The actual success depends on the user interacting with UAC triggered by the helper.
824
+ immediate_start_status = "start_success_helper_launched"
825
+ final_status = "success_helper_launched"
826
+
827
+ except FileNotFoundError as fnf_err:
828
+ # This might now be powershell.exe OR the helper script
829
+ self.log_error(f"Internal trigger: File not found during helper script launch: {fnf_err}")
830
+ immediate_start_status = "start_failed_file_not_found"
831
+ final_status = "failed_file_not_found"
832
+ except Exception as launch_err:
833
+ self.log_error(f"Internal trigger: Exception during CreateProcessAsUser for helper script (user '{user}'): {launch_err}", exc_info=True)
834
+ immediate_start_status = "start_failed_launch_exception"
835
+ final_status = "failed_launch_exception"
836
+ finally:
837
+ # Ensure token exists and is not None before attempting to close
838
+ if 'token' in locals() and token is not None:
839
+ try:
840
+ win32api.CloseHandle(token)
841
+ except: # Ignore errors closing handle
842
+ pass
811
843
 
812
844
  # Combine results (mostly determined by start attempt now)
813
- # Example: final_status = f"{task_created_status}_{immediate_start_status}"
845
+ # Status is now primarily determined by the helper script launch attempt
814
846
  return final_status
815
847
 
816
848
  except Exception as e:
@@ -818,35 +850,48 @@ class GuardService(win32serviceutil.ServiceFramework):
818
850
  return "failed_trigger_exception"
819
851
 
820
852
 
821
- def create_or_update_logon_task(self, username, port):
853
+ def create_or_update_logon_task(self, username):
822
854
  """Creates/updates task to trigger the internal signal script on session connect."""
823
- if not self.target_executable_path:
824
- self.log_error(f"Cannot create task for {username}: Target executable path not found.")
855
+ if not self.signal_script_path:
856
+ self.log_error(f"Cannot create task for {username}: Signal script path is not set.")
825
857
  return False
858
+ if not sys.executable:
859
+ self.log_error(f"Cannot create task for {username}: sys.executable is not found.")
860
+ return False
861
+
862
+ # Use the python executable that the service itself is running under
863
+ python_exe = sys.executable
864
+ if ' ' in python_exe and not python_exe.startswith('"'):
865
+ python_exe = f'"{python_exe}"'
826
866
 
827
867
  task_name = f"OOTB_UserConnect_{username}"
828
- target_exe_unquoted = self.target_executable_path.strip('"')
829
- target_exe_for_ps = target_exe_unquoted.replace("'", "''")
830
- action_arguments = f"--port {port} --target_user '{username}'".replace("'", "''") # Arguments for the target exe
831
-
832
- # Working directory for the executable
868
+ # Action: Run python.exe with the signal script and username argument
869
+ action_executable = python_exe
870
+ # Ensure script path is quoted if needed
871
+ script_arg = self.signal_script_path # Should be quoted already by _find_signal_script
872
+ # Username might need quoting if it contains spaces, though unlikely
873
+ user_arg = username # Keep simple for now
874
+ action_arguments = f'{script_arg} "{user_arg}"' # Pass username as quoted arg
875
+ safe_action_executable = action_executable.replace("'", "''") # Escape for PS
876
+ safe_action_arguments = action_arguments.replace("'", "''") # Escape for PS
877
+
878
+ # Working directory for the script (likely its own directory)
833
879
  try:
834
- target_dir = os.path.dirname(target_exe_unquoted)
835
- if not target_dir: target_dir = "."
836
- safe_working_directory = target_dir.replace("'", "''")
880
+ script_dir = os.path.dirname(self.signal_script_path.strip('"'))
881
+ if not script_dir: script_dir = "."
882
+ safe_working_directory = script_dir.replace("'", "''")
837
883
  working_directory_setting = f"$action.WorkingDirectory = '{safe_working_directory}'"
838
884
  except Exception as e:
839
- self.log_error(f"Error determining working directory for target exe task: {e}. WD will not be set.")
885
+ self.log_error(f"Error determining working directory for signal script task: {e}. WD will not be set.")
840
886
  working_directory_setting = "# Could not set WorkingDirectory"
841
887
 
842
888
  # PowerShell command construction
843
889
  ps_command = f"""
844
890
  $taskName = "{task_name}"
845
- # Principal runs as the user, but with highest privileges
846
- $principal = New-ScheduledTaskPrincipal -UserId "{username}" -LogonType Interactive -RunLevel Highest
891
+ $principal = New-ScheduledTaskPrincipal -UserId "{username}" -LogonType Interactive
847
892
 
848
- # Action: Run the target executable directly with arguments
849
- $action = New-ScheduledTaskAction -Execute '{target_exe_for_ps}' -Argument '{action_arguments}'
893
+ # Action: Run python signal script
894
+ $action = New-ScheduledTaskAction -Execute '{safe_action_executable}' -Argument '{safe_action_arguments}'
850
895
  {working_directory_setting}
851
896
 
852
897
  # Trigger: On session connect (Event ID 21)
@@ -916,6 +961,49 @@ class GuardService(win32serviceutil.ServiceFramework):
916
961
  self.log_info(f"Attempted removal of scheduled task '{task_name}' for user '{username}'.")
917
962
  return True
918
963
 
964
+ def _find_signal_script(self):
965
+ """Finds the signal_connection.py script relative to this service file."""
966
+ return self._find_helper_script("signal_connection.py") # Reuse helper finding logic
967
+
968
+ def _find_helper_script(self, script_name):
969
+ """Finds a helper script relative to this service file."""
970
+ try:
971
+ # Use __file__ which should be reliable when run as a service via pythonservice.exe
972
+ base_dir = os.path.dirname(os.path.abspath(__file__))
973
+ script_path = os.path.join(base_dir, script_name)
974
+ if os.path.exists(script_path):
975
+ self.log_info(f"Found helper script '{script_name}' at: {script_path}")
976
+ # Return unquoted path for potential quoting later
977
+ return script_path
978
+ else:
979
+ self.log_error(f"Helper script '{script_name}' not found near {base_dir}")
980
+ return None
981
+ except Exception as e:
982
+ self.log_error(f"Error finding helper script '{script_name}': {e}")
983
+ return None
984
+
985
+ def _find_powershell_executable(self):
986
+ """Finds powershell.exe, preferring the system path."""
987
+ try:
988
+ # Check System32 first
989
+ system32_path = os.path.join(os.environ.get('SystemRoot', 'C:\\Windows'), 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')
990
+ if os.path.exists(system32_path):
991
+ self.log_info(f"Found powershell.exe at: {system32_path}")
992
+ return system32_path
993
+ else:
994
+ # Fallback to checking PATH using shutil.which (requires Python 3.3+)
995
+ # Make sure to import shutil at the top of the file if not already present
996
+ powershell_path = shutil.which("powershell.exe")
997
+ if powershell_path:
998
+ self.log_info(f"Found powershell.exe via PATH: {powershell_path}")
999
+ return powershell_path
1000
+ else:
1001
+ self.log_error("powershell.exe not found in System32 or PATH.")
1002
+ return None
1003
+ except Exception as e:
1004
+ self.log_error(f"Error finding powershell.exe: {e}")
1005
+ return None
1006
+
919
1007
  # --- Main Execution Block ---
920
1008
  if __name__ == '__main__':
921
1009
  if len(sys.argv) > 1 and sys.argv[1] == 'debug':
@@ -0,0 +1,69 @@
1
+ # launch_ootb_elevated.ps1
2
+ param(
3
+ [Parameter(Mandatory=$true)]
4
+ [string]$TargetExePath,
5
+
6
+ [Parameter(Mandatory=$true)]
7
+ [string]$Port,
8
+
9
+ [Parameter(Mandatory=$true)]
10
+ [string]$TargetUser,
11
+
12
+ [Parameter(Mandatory=$true)]
13
+ [string]$WorkingDirectory
14
+ )
15
+
16
+ try {
17
+ Write-Host "--- OOTB Elevation Helper ---"
18
+ Write-Host "Timestamp: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
19
+ Write-Host "Received parameters:"
20
+ Write-Host " Target Exe Path : $TargetExePath"
21
+ Write-Host " Port : $Port"
22
+ Write-Host " Target User : $TargetUser"
23
+ Write-Host " Working Dir : $WorkingDirectory"
24
+ Write-Host ""
25
+
26
+ # Validate paths
27
+ if (-not (Test-Path -Path $TargetExePath -PathType Leaf)) {
28
+ throw "Target executable not found at '$TargetExePath'"
29
+ }
30
+ if (-not (Test-Path -Path $WorkingDirectory -PathType Container)) {
31
+ throw "Working directory not found at '$WorkingDirectory'"
32
+ }
33
+
34
+ # Construct the second argument for cmd.exe /K, precisely matching the working command
35
+ # Format: "<quoted_exe_path>" --port <port> --target_user ''<username>''
36
+ # Note: PowerShell handles escaping within double quotes differently.
37
+ # Using backticks ` before inner quotes is safer here.
38
+ $cmdKSecondArg = "`"`"$TargetExePath`"`" --port $Port --target_user ''$TargetUser''"
39
+
40
+ # Construct the argument list array for Start-Process launching cmd.exe
41
+ $startProcessArgs = @('/K', $cmdKSecondArg)
42
+
43
+ Write-Host "Constructed cmd.exe /K arguments: $cmdKSecondArg"
44
+ Write-Host "Constructed Start-Process arguments array: $($startProcessArgs -join ' ')" # For logging
45
+ Write-Host "Executing elevated process..."
46
+ Write-Host "Command: Start-Process -FilePath cmd.exe -ArgumentList @('/K', '$cmdKSecondArg') -WorkingDirectory '$WorkingDirectory' -Verb RunAs"
47
+ Write-Host "--- Waiting for UAC prompt if necessary ---"
48
+
49
+ # Execute the command to launch cmd elevated, which runs the target app
50
+ Start-Process -FilePath cmd.exe -ArgumentList $startProcessArgs -WorkingDirectory $WorkingDirectory -Verb RunAs
51
+
52
+ Write-Host "--- OOTB Elevation Helper: Start-Process command executed. ---"
53
+ # The calling powershell window (started by CreateProcessAsUser with -NoExit) will remain open.
54
+
55
+ } catch {
56
+ Write-Error "--- OOTB Elevation Helper Error ---"
57
+ Write-Error "Error launching elevated process: $($_.Exception.Message)"
58
+ Write-Error "Script Parameters:"
59
+ Write-Error " Target Exe Path : $TargetExePath"
60
+ Write-Error " Port : $Port"
61
+ Write-Error " Target User : $TargetUser"
62
+ Write-Error " Working Dir : $WorkingDirectory"
63
+ Write-Host "Press Enter to exit..."
64
+ Read-Host # Keep window open on error
65
+ Exit 1
66
+ }
67
+
68
+ # Exit gracefully if successful
69
+ Exit 0