Foro de elhacker.net

Programación => Scripting => Mensaje iniciado por: retr02332 en 27 Enero 2020, 21:23 pm



Título: ¿Cuál es la mejor manera de ocultar un intérprete de Python?
Publicado por: retr02332 en 27 Enero 2020, 21:23 pm
Hola a todos

¿ Es mejor usar el argumento "noconsole" como parámetro en el PyInstaller ?, ¿ O es mejor usar:

window = win32console.GetConsoleWindow ()

win32gui.ShowWindow (window, 0)

???

Muchas gracias.


Título: Re: ¿Cuál es la mejor manera de ocultar un intérprete de Python?
Publicado por: Eleкtro en 27 Enero 2020, 22:24 pm
Con la opción de utilizar la API de Windows en tiempo de eejcución siempre existirá un (a veces inperceptible) "parpadeo" de la ventana ya que se entiende que harás esas llamadas al inicio del programa despues de que la ventana ya haya sido creada, mientras que la opción de utilizar el parámetro "noconsole" en Pyinstaller es más directo y sofisticado ya que el bootloader pasará el valor apropiado al parámetro 'nShowCmd' en la creación del proceso/ventana (el equivalente a ShowWindow(HWND, SW_HIDE)):
  • https://github.com/pyinstaller/pyinstaller/blob/9882f871ad27e107b3fdbac242713c6773d121a1/bootloader/src/main.c#L51
  • https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winmain?redirectedfrom=MSDN

y adicionalmente también se suprime la escritura de mensajes en la salida estándar y salida de error (Std-Out, Std-Err) de la ventana oculta para evitar posibles errores conocidos según parece darse a entender en los comentarios de la documentación:
  • https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/loader/pyiboot01_bootstrap.py#L67

Saludos.


Título: Re: ¿Cuál es la mejor manera de ocultar un intérprete de Python?
Publicado por: retr02332 en 27 Enero 2020, 23:09 pm
Veamos si entendi.

Con el Win32, este tendra un parpadeo, ya que este hace la llamada a la api en tiempo de ejecucion, mientras que con "noconsole", esto no pasara.

Ademas si uso el api, y por alguna razon sucede un error en la ejecucion o cualquier otro, aparecera una terminal en plena pantalla, al igual que si automatizo comandos, se vera como se escriben en la terminal, de nuevo (aparecera una terminal en plena pantalla).

Mientras que con "noconsole", en caso de error, o por ejemplo, una automatizacion de comandos, estos no se veran en ningun momento en la pantalla, no aparecera una terminal de repente en la pc.

¿Estoy en lo correcto?

Gracias, saludos.


Título: Re: ¿Cuál es la mejor manera de ocultar un intérprete de Python?
Publicado por: Eleкtro en 28 Enero 2020, 00:12 am
Con el Win32, este tendra un parpadeo, ya que este hace la llamada a la api en tiempo de ejecucion, mientras que con "noconsole", esto no pasara.

Así debería ser según lo que pude analizar en el código fuente de Pyinstaller y su bootloader. No utilizo habitualmente Python ni Pyinstaller, así que simplemente son conjeturas, pero son muy fundadas y evidentes en el código fuente, pues Pyinstaller utiliza como bootloader un programa desarrollado en C para iniciar Python, y en cuyo punto de entrada o entry point se especifica que la ventana no es visible.

Ademas si uso el api, y por alguna razon sucede un error en la ejecucion o cualquier otro, aparecera una terminal en plena pantalla, al igual que si automatizo comandos, se vera como se escriben en la terminal, de nuevo (aparecera una terminal en plena pantalla).

No, una excepción no controlada en un programa (ej. intérprete de Python) no provoca que la ventana(principal) oculta se vuelva visible, de todas formas no es esto a lo que yo me refería (sigue leyendo)...

Tampoco se mostrará la ventana del intérprete por imprimir mensajes, pero obviamente la impresión de mensajes es innecesaria puesto que la ventana estará oculta y los mensajes no se podrán leer, y aquí viene el problema: el intento de escritura/impresión de mensajes en una ventana oculta además puede ocasionar un error en ciertas circunstancias (que están descritas en los comentarios del código fuente y que al parecer solo afecta al intérprete de Python 2.x) al intentar vaciar el búfer de salida. Pyinstaller en teoría es capaz de evitar este tipo de error asignando al búfer Std-Out y Std-Err una clase 'NullWriter' la cual sustituye las funciones de escritura de mensajes y vaciado del búfer de salida por funciones que literalmente no hacen nada:

Código
  1. if sys.stdout is None:
  2.  sys.stdout = NullWriter()
  3. if sys.stderr is None:
  4.  sys.stderr = NullWriter()

Código
  1. class NullWriter:
  2.  ...
  3.  def write(*args):
  4.    pass
  5.  
  6.  def flush(*args):
  7.    pass
  8.  ...

Por todo esto dije que es más sofisticado. Si usas un wrapper de la función 'ShowWindow' de la API de Windows estarás modificando una ventana actualmente visible para ocultarla (lo que producirá un "parpadeo" si llamas a esta función justo al inicio del programa, puesto que al momento de crear la ventana, esta se mostrará por un instante, y luego se ocultará), y eso será todo lo que harás, pero si en cambio te limitas a usar el parámetro 'noconsole' en Pyinstaller entonces la ventana directamente no se debería llegar a mostrar (ni por un microsegundo), y además se llevarán a cabo optimizaciones con respecto a la prevención de errores en el código en ejecución de Python.

Saludos.