elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


Tema destacado: ¿Eres usuario del foro? Ahora tienes un Bot con IA que responde preguntas. Lo puedes activar en tu Perfil


  Mostrar Temas
Páginas: [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... 107
1  Sistemas Operativos / Windows / ¿Alguna buena ISO lite personalizada de Windows XP? en: 28 Diciembre 2025, 11:53 am
¡Hola! Me preguntaba si alguien sabría indicarme dónde descargar una ISO personalizada de Windows XP SP3 ya equipada con todo el software y runtimes básicos para poder hacer un uso medianamente útil de este sistema operativo sin que se rompa.

No busco una ISO de Windows XP SP3 llena de cientos de utilidades en plan Live CD, eso no me interesa. Más bien busco una ISO preparada con lo esencial, para poder correr bien, por ejemplo que ya tenga integrado un navegador funcional, por que el Internet Explorer integrado en Windows XP ni siquiera Google lo soporta ya...

El propósito inicial sería poder disponer de una ISO "lite" de Windows XP SP3 en una máquina virtual, para diversos usos.

Ah, por cierto, me es indiferente si está en inglés o en español. Pero en ruso no, por favor, que no entendería nada xD

Gracias por adelantado.
2  Programación / Scripting / [APORTE] [PowerShell] Modificar aleatoriamente el checksum de un archivo ejecutable en: 21 Septiembre 2025, 15:07 pm
El siguiente script, desarrollado en PowerShell, sirve para modificar de forma aleatoria el checksum actual de un archivo ejecutable (formato PE).

Este procedimiento podría ser útil, por ejemplo, para evitar comprobaciones simples de integridad de archivos o verificaciones de firma que dependen de valores de checksum fijos, como los que pudieran estar implementados por sistemas anti-trampas en videojuegos y otro software de protección.

Para cumplir con este objetivo, el script cubre dos técnicas combinadas. La primera consiste en inyectar un valor aleatorio en el campo opcional de checksum del encabezado PE. Esta técnica es necesaria para alterar el resultado de la validación de la API de Windows 'MapFileAndCheckSum', ya que no calcula el checksum de forma corriente sobre todo el archivo, sino que utiliza el valor presente en ese campo del PE.

La segunda técnica consiste en agregar una cantidad pequeña de bytes de relleno o padding (entre 4 y 16 bytes) al final del archivo para alterar el tamaño del mismo, y por consiguiente el cálculo del checksum del archivo, sin afectar a la funcionalidad del ejecutable.







El mismo archivo "reabierto" tras la modificación:


⚠️ Importante: no utilizar el script con archivos PE que tengan un certificado digital, ya que el archivo se corromperá.

24 de septiembre de 2025: Script actualizado para identificar archivos PE que contienen una tabla de certificados y abortar la operación de inmediato, evitando así la posible corrupción del ejecutable.

Aunque considero haber probado el código lo suficiente, no me hago responsable de posibles daños causados al intentar modificar un archivo. Hagan siempre una copia de seguridad antes de modificar un archivo. 👍



Randomize executable checksum.ps1

El código fuente

Código
  1. <#PSScriptInfo
  2. .VERSION 1.1
  3. .GUID A1B2C3D4-E5F6-7890-ABCD-EF1234567890
  4. .AUTHOR ElektroStudios
  5. .COMPANYNAME ElektroStudios
  6. .COPYRIGHT ElektroStudios © 2025
  7. #>
  8.  
  9. <#
  10. ===========================================================================================
  11. |                                                                                         |
  12. |                                       User Fields                                       |
  13. |                                                                                         |
  14. ===========================================================================================
  15. #>
  16.  
  17. # Path to the executable file to randomize its header and file checksums.
  18. $exePath = ".\MyExecutable.exe"
  19.  
  20. <#
  21. ===========================================================================================
  22. |                                                                                         |
  23. |                                        .NET Code                                        |
  24. |                                                                                         |
  25. ===========================================================================================
  26. #>
  27.  
  28. Add-Type @"
  29. using System;
  30. using System.Runtime.InteropServices;
  31.  
  32. public static class NativeMethods {
  33.    [DllImport("imagehlp.dll", SetLastError = true)]
  34.    public static extern uint MapFileAndCheckSum(string filename, out uint headerSum, out uint checkSum);
  35. }
  36. "@
  37.  
  38. Add-Type -TypeDefinition @"
  39. using System;
  40.  
  41. public class CRC32
  42. {
  43.    private static readonly uint[] Table = new uint[256];
  44.    private uint crc;
  45.  
  46.    static CRC32()
  47.    {
  48.        uint poly = 0xEDB88320;
  49.        for (uint i = 0; i < 256; i++)
  50.        {
  51.            uint temp = i;
  52.            for (int j = 0; j < 8; j++)
  53.            {
  54.                if ((temp & 1) == 1)
  55.                    temp = (temp >> 1) ^ poly;
  56.                else
  57.                    temp >>= 1;
  58.            }
  59.            Table[i] = temp;
  60.        }
  61.    }
  62.  
  63.    public CRC32()
  64.    {
  65.        crc = 0xFFFFFFFF;
  66.    }
  67.  
  68.    public void Update(byte[] buffer, int offset, int count)
  69.    {
  70.        for (int i = offset; i < offset + count; i++)
  71.        {
  72.            byte index = (byte)((crc ^ buffer[i]) & 0xFF);
  73.            crc = (crc >> 8) ^ Table[index];
  74.        }
  75.    }
  76.  
  77.    public uint Compute(byte[] buffer)
  78.    {
  79.        Update(buffer, 0, buffer.Length);
  80.        return crc ^ 0xFFFFFFFF;
  81.    }
  82.  
  83.    public static uint ComputeChecksum(byte[] buffer)
  84.    {
  85.        CRC32 crc32 = new CRC32();
  86.        return crc32.Compute(buffer);
  87.    }
  88. }
  89. "@
  90.  
  91. <#
  92. ===========================================================================================
  93. |                                                                                         |
  94. |                                    Functions                                            |
  95. |                                                                                         |
  96. ===========================================================================================
  97. #>
  98.  
  99. function Show-WelcomeScreen {
  100.    Clear-Host
  101.    Write-Host ""
  102.    Write-Host " $($host.ui.RawUI.WindowTitle)"
  103.    Write-Host " +================================================================+"
  104.    Write-Host " |                                                                |"
  105.    Write-Host " | This script modifies the file checksum of the specified        |"
  106.    Write-Host " | executable file (PE format) by injecting a new random header   |"
  107.    Write-Host " | checksum field and appending random padding bytes to alter the |"
  108.    Write-Host " | file checksum without affecting the executable's functionality.|"
  109.    Write-Host " |                                                                |"
  110.    Write-Host " | This process is useful to bypass simple file integrity checks  |"
  111.    Write-Host " | or signature verifications that rely on fixed checksum values, |"
  112.    Write-Host " | such as those implemented by anti-cheat videogaming systems    |"
  113.    Write-Host " | and similar software protection agents.                        |"
  114.    Write-Host " +================================================================+"
  115.    Write-Host ""
  116.    Write-Host " Script Settings " -ForegroundColor DarkGray
  117.    Write-Host " ================" -ForegroundColor DarkGray
  118.    Write-Host " Executable Path: $([System.IO.Path]::GetFullPath($exePath))" -ForegroundColor DarkGray
  119.    Write-Host ""
  120. }
  121.  
  122. function Confirm-Continue {
  123.    Write-Host "Press 'Y' key to continue or 'N' to exit."
  124.    Write-Host ""
  125.    Write-Host "-Continue? (Y/N)"
  126.    do {
  127.        $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
  128.        $char = $key.Character.ToString().ToUpper()
  129.        if ($char -ne "Y" -and $char -ne "N") {
  130.            [console]::beep(1500, 500)
  131.        }
  132.    } while ($char -ne "Y" -and $char -ne "N")
  133.    if ($char -eq "N") {Exit(0)} else {Clear-Host}
  134. }
  135.  
  136. function Read-UInt16($bytes, $offset) {
  137.    if ($offset -lt 0 -or $offset + 2 -gt $bytes.Length) {
  138.        throw "Offset $offset out of range for Read-UInt16"
  139.    }
  140.    return [BitConverter]::ToUInt16($bytes, $offset)
  141. }
  142.  
  143. function Read-UInt32($bytes, $offset) {
  144.    if ($offset -lt 0 -or $offset + 4 -gt $bytes.Length) {
  145.        throw "Offset $offset out of range for Read-UInt32"
  146.    }
  147.    return [BitConverter]::ToUInt32($bytes, $offset)
  148. }
  149.  
  150. function Write-UInt32([byte[]]$bytes, $offset, [uint32]$value) {
  151.    if ($offset -lt 0 -or $offset + 4 -gt $bytes.Length) {
  152.        throw "Offset $offset out of range for Write-UInt32"
  153.    }
  154.    $valBytes = [BitConverter]::GetBytes($value)
  155.    [Array]::Copy($valBytes, 0, $bytes, $offset, 4)
  156. }
  157.  
  158. function Randomize-ExecutableChecksum{
  159.    param(
  160.        [string]$exePath
  161.    )
  162.  
  163.    # Read the raw file bytes
  164.    if (-Not (Test-Path $exePath)) {
  165.        Show-GoodbyeScreen "File path '$([System.IO.Path]::GetFullPath($exePath))' does not exist." ([System.ConsoleColor]::Red)
  166.    }
  167.    [byte[]]$bytes = [System.IO.File]::ReadAllBytes($exePath)
  168.  
  169.    # Locate e_lfanew (pointer to PE header)
  170.    $e_lfanew = Read-UInt32 $bytes 0x3C
  171.    # Check that e_lfanew is within valid range (at least 4 bytes from the end)
  172.    if ($e_lfanew -ge $bytes.Length - 4) {
  173.        Show-GoodbyeScreen "Invalid e_lfanew or corrupt file." ([System.ConsoleColor]::Red)
  174.    }
  175.  
  176.    # Read and validate the PE signature (should be "PE\0\0") at the offset pointed by e_lfanew
  177.    $peSignature = [System.Text.Encoding]::ASCII.GetString($bytes, $e_lfanew, 4)
  178.    if ($peSignature -ne "PE`0`0") {
  179.        Show-GoodbyeScreen "Not a valid PE file (PE signature not found)." ([System.ConsoleColor]::Red)
  180.    }
  181.  
  182.    # Calculate Optional Header offset (PE signature + File Header)
  183.    $optionalHeaderOffset = $e_lfanew + 4 + 20
  184.  
  185.    # Read Magic field in the Optional Header that determines PE32/PE32+
  186.    $magic = Read-UInt16 $bytes $optionalHeaderOffset
  187.    if ($magic -eq 0x10b) {
  188.        # PE32 (32-bit)
  189.        $checksumOffset = $optionalHeaderOffset + 64
  190.        $dataDirectoryOffset = $optionalHeaderOffset + 96
  191.    } elseif ($magic -eq 0x20b) {
  192.        # PE32+ (64-bit)
  193.        $checksumOffset = $optionalHeaderOffset + 64
  194.        $dataDirectoryOffset = $optionalHeaderOffset + 112
  195.    } else {
  196.        Show-GoodbyeScreen ("Unknown or unsupported PE format. Magic = 0x{0:X}" -f $magic) ([System.ConsoleColor]::Red)
  197.    }
  198.  
  199.    # Read Certificate Table directory (DataDirectory[4])
  200.    $certSize = Read-UInt32 $bytes ($dataDirectoryOffset + 4*8 + 4) # IMAGE_DATA_DIRECTORY::Size
  201.    if ($certSize -gt 0) {
  202.        Show-GoodbyeScreen "This PE file has a certificate table. Operation aborted to avoid breaking PE signature." ([System.ConsoleColor]::Red)
  203.    }
  204.  
  205.    # Calculate the actual CRC32 file checksum
  206.    $currentCRC32 = '{0:x8}' -f ([CRC32]::ComputeChecksum($bytes))
  207.  
  208.    # Calculate the actual header and file checksums using Windows API
  209.    [uint32]$currentHeaderSum = 0
  210.    [uint32]$currentFileSum = 0
  211.    [NativeMethods]::MapFileAndCheckSum($exePath, [ref]$currentHeaderSum, [ref]$currentFileSum) | Out-Null
  212.  
  213.    # Generate a new random header checksum value
  214.    $randomBytes = New-Object byte[] 4
  215.    [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($randomBytes)
  216.    $newHeaderSum = [BitConverter]::ToUInt32($randomBytes, 0)
  217.  
  218.    # Write the new random header checksum value into the file bytes
  219.    Write-UInt32 $bytes $checksumOffset $newHeaderSum
  220.  
  221.    # Add useless padding bytes at the end (overlay) of the file bytes to generate a new file checksum
  222.    $paddingLength = Get-Random -Minimum 4 -Maximum 16
  223.    $padding = New-Object byte[] $paddingLength
  224.    [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($padding)
  225.    $newBytes = New-Object byte[] ($bytes.Length + $paddingLength)
  226.    [Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length)
  227.    [Array]::Copy($padding, 0, $newBytes, $bytes.Length, $paddingLength)
  228.  
  229.    # Calculate the new CRC32 file checksum
  230.    $newCRC32 = '{0:x8}' -f ([CRC32]::ComputeChecksum($newBytes))
  231.  
  232.    Write-Host "========== CHECKSUM INFORMATION ==========" -ForegroundColor Cyan
  233.    Write-Host "File: $exePath" -ForegroundColor Yellow
  234.    Write-Host ""
  235.    Write-Host "Header checksum field" -ForegroundColor Cyan
  236.    Write-Host "=====================" -ForegroundColor Gray
  237.    Write-Host "Current    : $currentHeaderSum" -ForegroundColor White
  238.    Write-Host "Replacement: $newHeaderSum" -ForegroundColor White
  239.    Write-Host ""
  240.    Write-Host "Calculated CRC-32 file checksum" -ForegroundColor Cyan
  241.    Write-Host "===============================" -ForegroundColor Gray
  242.    Write-Host "Current    : 0x$($currentCRC32.ToUpper())" -ForegroundColor White
  243.    Write-Host "Replacement: 0x$($newCRC32.ToUpper())" -ForegroundColor White
  244.    Write-Host ""
  245.    Write-Host "Calculated file checksum via 'MapFileAndCheckSum' API" -ForegroundColor Cyan
  246.    Write-Host "=====================================================" -ForegroundColor Gray
  247.    Write-Host "Current    : $currentFileSum" -ForegroundColor White
  248.    Write-Host "Replacement: Can't be calculated until the" -ForegroundColor White
  249.    Write-Host "             new header checksum field is" -ForegroundColor White
  250.    Write-Host "             written to the actual file." -ForegroundColor White
  251.    Write-Host ""
  252.    Write-Host "-----------------------------------------------------" -ForegroundColor Gray
  253.    Write-Host ""
  254.  
  255.    Confirm-Continue
  256.  
  257.    try {
  258.        # Replace the source file by writing the changed bytes, containing the new header checksum and the padding bytes.
  259.        [System.IO.File]::WriteAllBytes($exePath, $newBytes)
  260.    } catch {
  261.        Show-GoodbyeScreen "Failed to write the modified bytes to the actual file. Please check file permissions and path." ([System.ConsoleColor]::Red)
  262.    }
  263.  
  264.    # Calculate the new file checksum using Windows API
  265.    [uint32]$newFileSum = 0
  266.    [NativeMethods]::MapFileAndCheckSum($exePath, [ref]0, [ref]$newFileSum) | Out-Null
  267.  
  268.    Write-Host "========== CHECKSUM INFORMATION ==========" -ForegroundColor Cyan
  269.    Write-Host "File: $exePath" -ForegroundColor Yellow
  270.    Write-Host ""
  271.    Write-Host "Header checksum field" -ForegroundColor Cyan
  272.    Write-Host "=====================" -ForegroundColor Gray
  273.    Write-Host "Previous: $currentHeaderSum" -ForegroundColor White
  274.    Write-Host "Current : $newHeaderSum" -ForegroundColor White
  275.    Write-Host ""
  276.    Write-Host "Calculated CRC-32 file checksum" -ForegroundColor Cyan
  277.    Write-Host "===============================" -ForegroundColor Gray
  278.    Write-Host "Previous: 0x$($currentCRC32.ToUpper())" -ForegroundColor White
  279.    Write-Host "Current : 0x$($newCRC32.ToUpper())" -ForegroundColor White
  280.    Write-Host ""
  281.    Write-Host "Calculated file checksum via 'MapFileAndCheckSum' API" -ForegroundColor Cyan
  282.    Write-Host "=====================================================" -ForegroundColor Gray
  283.    Write-Host "Previous: $currentFileSum" -ForegroundColor White
  284.    Write-Host "Current : $newFileSum" -ForegroundColor White
  285.    Write-Host ""
  286.    Write-Host "-----------------------------------------------------" -ForegroundColor Gray
  287.    Write-Host ""
  288. }
  289.  
  290. function Show-GoodbyeScreen{
  291.    param(
  292.        [string]$msg,
  293.        [string]$forecolor = "White"
  294.    )
  295.  
  296.    Write-Host $msg -BackgroundColor Black -ForegroundColor $forecolor
  297.    Write-Host ""
  298.    Write-Host "Press any key to exit..."
  299.    $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown")
  300.    Exit(0)
  301. }
  302.  
  303. <#
  304. ===========================================================================================
  305. |                                                                                         |
  306. |                                         Main                                            |
  307. |                                                                                         |
  308. ===========================================================================================
  309. #>
  310.  
  311. [System.Console]::Title = "Randomize executable checksum - by Elektro"
  312. #[System.Console]::SetWindowSize(150, 45)
  313. [CultureInfo]::CurrentUICulture = "en-US"
  314.  
  315. if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
  316.    Write-Host "Please run this script as Administrator!" -ForegroundColor Red
  317.    Pause
  318.    exit
  319. }
  320.  
  321. try { Set-ExecutionPolicy -ExecutionPolicy "Unrestricted" -Scope "Process" } catch { }
  322.  
  323. Show-WelcomeScreen
  324. Confirm-Continue
  325. Randomize-ExecutableChecksum $exePath
  326. Show-GoodbyeScreen "Operation Completed!" ([System.ConsoleColor]::Green)



Aspectos a tener en cuenta antes de usar

La configuración del script está definida directamente en el código y no admite parámetros por línea de comandos.

Citar
Código
  1. <#
  2. ===========================================================================================
  3. |                                                                                         |
  4. |                                       User Fields                                       |
  5. |                                                                                         |
  6. ===========================================================================================
  7. #>
  8.  
  9. # Path to the executable file to randomize its header and file checksums.
  10. $exePath = ".\MyExecutable.exe"

Atentamente,
Elektro. 👋
3  Programación / Scripting / [APORTE] [PowerShell] [VBS] Mostrar el tiempo transcurrido desde el último arranque del sistema. en: 8 Septiembre 2025, 00:57 am
El siguiente script, desarrollado en PowerShell, crea una ventana gráfica (Form) que muestra, en tiempo real, el tiempo transcurrido desde el último arranque (uptime) del sistema:


( Nota: el efecto de parpadeo o flickering es debido a la captura del GIF animado )

Es un script muy simple y su único cometido es ese. Yo lo utilizo en una máquina virtual, aunque cada persona podría encontrarle usos diferentes.

El código:
Código
  1. Add-Type -AssemblyName System.Drawing
  2. Add-Type -AssemblyName System.Windows.Forms
  3.  
  4. Add-Type @"
  5. using System;
  6. using System.Runtime.InteropServices;
  7.  
  8. public static class WinAPI {
  9.    [DllImport("kernel32.dll")]
  10.    public static extern IntPtr GetConsoleWindow();
  11.  
  12.    [DllImport("user32.dll")]
  13.    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
  14.  
  15.    [DllImport("shell32.dll", CharSet=CharSet.Unicode)]
  16.    public static extern int ExtractIconEx(string lpszFile, int nIconIndex, out IntPtr phiconLarge, out IntPtr phiconSmall, int nIcons);
  17.  
  18.    [DllImport("user32.dll", CharSet=CharSet.Auto)]
  19.    public static extern bool DestroyIcon(IntPtr handle);
  20. }
  21. "@
  22.  
  23. # --- SINGLE INSTANCE CHECK THROUGH MUTEX ---
  24. $mutexName = "Global\ComputerUptimeFormMutex"
  25. $createdNew = $false
  26. $mutex = New-Object System.Threading.Mutex($true, $mutexName, [ref]$createdNew)
  27.  
  28. if (-not $createdNew) {
  29.    [System.Windows.Forms.MessageBox]::Show(
  30.        "Only one instance of this program is allowed.",
  31.        "Computer Uptime",
  32.        [System.Windows.Forms.MessageBoxButtons]::OK,
  33.        [System.Windows.Forms.MessageBoxIcon]::Stop
  34.    )
  35.    exit
  36. }
  37.  
  38. # --- HIDE CURRENT POWERSHELL CONSOLE ---
  39. $SW_HIDE = 0
  40. $hWnd = [WinAPI]::GetConsoleWindow()
  41. [WinAPI]::ShowWindow($hWnd, $SW_HIDE) | Out-Null
  42.  
  43. # --- CREATE THE FORM ---
  44. $form                 = New-Object System.Windows.Forms.Form
  45. $form.Text            = "Computer Uptime"
  46. $form.Size            = New-Object System.Drawing.Size(350, 150)
  47. $form.StartPosition   = "CenterScreen"
  48. $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
  49. $form.MaximizeBox     = $false
  50. $form.MinimizeBox     = $true
  51. $form.Padding         = New-Object System.Windows.Forms.Padding(4)
  52. $form.DoubleBuffered  = $true
  53.  
  54. # --- SET FORM ICON ---
  55. $shell32   = "$env:SystemRoot\System32\shell32.dll"
  56. $hLarge    = [IntPtr]::Zero
  57. $hSmall    = [IntPtr]::Zero
  58. $iconIndex = 265 # A clock icon in Windows 10.
  59.  
  60. [WinAPI]::ExtractIconEx($shell32, $iconIndex, [ref]$hLarge, [ref]$hSmall, 1) | Out-Null
  61.  
  62. if ($hSmall -ne [IntPtr]::Zero) {
  63.    $form.Icon = [System.Drawing.Icon]::FromHandle($hSmall)
  64. }
  65.  
  66. # --- LABEL TO DISPLAY UPTIME ---
  67. $label                = New-Object System.Windows.Forms.Label
  68. $label.Font           = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold)
  69. $label.Dock           = [System.Windows.Forms.DockStyle]::Fill
  70. $label.TextAlign      = [System.Drawing.ContentAlignment]::MiddleCenter
  71. $label.AutoSize       = $false
  72. $label.DoubleBuffered = $true
  73. $form.Controls.Add($label)
  74.  
  75. # --- GET SYSTEM INFORMATION FROM WMI ---
  76. $os           = Get-CimInstance Win32_OperatingSystem
  77. $bootTime     = $os.LastBootUpTime
  78. $computerName = $os.CSName
  79.  
  80. # --- TIMER TO UPDATE UPTIME ---
  81. $timer = New-Object System.Windows.Forms.Timer
  82. $timer.Interval = 100
  83. $timer.Add_Tick({
  84.    $uptime       = (Get-Date) - $bootTime
  85.    $minutes      = $uptime.Minutes.ToString("00")
  86.    $seconds      = $uptime.Seconds.ToString("00")
  87.    $milliseconds = $uptime.Milliseconds.ToString("000")
  88.    $label.Text   = "$computerName`n`n$($uptime.Days) days — $($uptime.Hours)h : $($minutes)m : $($seconds)s : $($milliseconds)ms"
  89. })
  90. $timer.Start()
  91.  
  92. # --- RELEASE ICON HANDLES AND MUTEX WHEN FORM GETS CLOSED ---
  93. $form.Add_FormClosed({
  94.    if ($hSmall -ne [IntPtr]::Zero) { [WinAPI]::DestroyIcon($hSmall) }
  95.    if ($hLarge -ne [IntPtr]::Zero) { [WinAPI]::DestroyIcon($hLarge) }
  96.    $mutex.ReleaseMutex()
  97. })
  98.  
  99. # --- SHOW THE FORM ---
  100. [void]$form.ShowDialog()

PD: 80% del código fue hecho por ChatGPT (considero una pérdida de tiempo diseñar manualmente el form en texto plano, además de buscar y añadir las definiciones de la API de Windows, cosas que puede hacer una IA perfectamente y en menos de un segundo), 20% edición y revisión humana. De todas formas, esto no tendría ningún mérito haberlo hecho a mano en un 100%, pero aun así quiero ser honesto con lo que comparto.



Por último, les muestro una especie de equivalente mucho más básico hecho con VisualBasic Script (VBS). El siguiente código tan solo muestra un cuadro de mensaje, sin actualización en tiempo real de ningún tipo.

Código
  1. Option Explicit
  2.  
  3. Dim oneMinute, oneHour, oneDay: oneMinute = 60: oneHour = 3600: oneDay = 86400
  4. Dim objWMIService, colOperatingSystems, objOperatingSystem
  5. Dim computerName, lastBootUpTime, upTime
  6.  
  7. Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
  8. Set colOperatingSystems = objWMIService.ExecQuery("Select * from Win32_OperatingSystem")
  9.  
  10. For Each objOperatingSystem In colOperatingSystems
  11.    computerName = objOperatingSystem.CSName
  12.    lastBootUpTime = objOperatingSystem.LastBootUpTime
  13.    lastBootUpTime = CDate(Mid(lastBootUpTime, 1, 4) & "/" & Mid(lastBootUpTime, 5, 2) & "/" & Mid(lastBootUpTime, 7, 2) & " " & _
  14.                           Mid(lastBootUpTime, 9, 2) & ":" & Mid(lastBootUpTime, 11, 2) & ":" & Mid(lastBootUpTime, 13, 2))
  15.  
  16.    upTime = DateDiff("s", lastBootUpTime, Now)
  17.    MsgBox computerName & vbCrLf & vbCrLf  & _
  18.           upTime \ oneDay & " days ~ " & _
  19.           (upTime Mod oneDay) \ oneHour & "h : " & _
  20.           (upTime Mod oneHour) \ oneMinute & "m : " & _
  21.           upTime Mod oneMinute & "s", vbInformation, "Computer Uptime"
  22.  
  23. Next
  24.  
  25. WScript.Quit(0)
4  Foros Generales / Sugerencias y dudas sobre el Foro / ¿Han recibido mi e-mail? en: 7 Septiembre 2025, 22:20 pm
Hola. Envié un correo a varias direcciones que supuestamente son del staff de elhacker.net, lo siento por parecer pesado pero me quitarían una preocupación de encima si alguien me confirmase que han recibido el correo. Por que no sé si en alguna (o todas) esas direcciones de correo tal vez me tienen bloqueado por discusiones ocurridas en el pasado.

El e-mail que les he enviado es en relación a mi petición para eliminar un e-mail que aparece en un post del sitio web https://forum.elhacker.net/, y ahí explico todos los detalles...

Este es el segundo hilo que abro al respecto, y sé que solo han pasado dos días, pero me preocupo con facilidad, sobre todo cuando el primer hilo lo han borrado sin ofrecer respuesta, y por el momento nadie se ha puesto en contacto conmigo. (¿ustedes suelen fijarse en los hilos borrados de la papelera?).

Por favor tengan en cuenta que yo desconozco quien tiene acceso para administrar ese sitio web, no sé si solamente el-brujo es capaz, y a lo mejor por eso nadie ha querido ofrecerme una respuesta ni contactar conmigo. En ese caso díganmelo y contactaré con él por WhatsApp, como ya os dije en una ocasión no quiero recurrir a eso sin su consentimiento para terminar molestando... si no fuese realmente necesario.

Ya he enviado la correspondiente solicitud de retirada de contenido a Google para ver si ellos pueden eliminar ese resultado de búsqueda donde aparece el post de https://forum.elhacker.net/, pero desconozco cuanto tiempo puede tardar en resolverse este tipo de denuncia, y al final pueden decidir no hacer nada al respecto, así que por favor necesito que ustedes me ayuden con lo que está en vuestras manos poder hacer.

Quiero pensar que en esta ocasión no me perciben como Elektro "el que le cae mal a todo el staff", sino como una persona que simplemente solicita algo tan razonable como poder eliminar cierta información personal que no debería ser accesible de forma pública como lo es desde ese sitio web, y a su vez desde un motor de búsqueda tipo Google.

Si ustedes revocasen mi baneo en ese sitio web, y suponiendo que yo pudiera editar un post de esa antigüedad, pues podría hacer yo mismo la tarea de eliminar ese e-mail y así no les quitaría más tiempo con este asunto.

Gracias de antemano.
5  Foros Generales / Foro Libre / 🧠 La esencia del debate en: 13 Julio 2025, 01:37 am
Hola, compañeros del foro, simplemente vine a compartir la siguiente reflexión con los usuarios y participantes del foro libre:



La esencia del debate

Debatir no es lanzar una idea esperando que todos coincidan con tus creencias, asientan y te aplaudan. Tampoco es enfadarse si alguien no coincide contigo, ni sentirse insultado o atacado al primer desacuerdo y señalarle con el dedo. Eso no es debate, es buscar validación emocional en lugar de un intercambio intelectual.

Todo el mundo tiene la capacidad de participar en un debate, porque, en definitiva, es suficiente con expresar tu opinión frente a los demás. Sin embargo, no todo el mundo está preparado para debatir con madurez y responsabilidad.

Abrir un hilo destinado al debate implica la predisposición de crear un espacio donde se debe estar dispuesto a que distintas perspectivas coexistan y sean escuchadas. Es un acto que requiere disposición, tolerancia y carácter, con la capacidad de aceptar que otros piensen diferente a ti y, aun así, mantener un diálogo respetuoso y constructivo, con el fin de descubrir otras posturas y, tal vez, poder aprender algo nuevo de ellas.

El valor de escuchar, y coincidir o disentir para evolucionar

El verdadero valor del debate está en aceptar que alguien puede pensar distinto a ti, y sin embargo, mostrar tu respeto, tu atención y tu disposición a escuchar su opinión. Es tolerar esas ideas contrarias, sin necesidad de compartirlas, sin necesidad de rebatirlas agresivamente, y sin caer en la tentación de imponer las tuyas propias como si fuesen verdades absolutas.

Y escuchar no implica renunciar a tus ideas, sino abrirte a comprender las ideas de los demás, tomar esas informaciones y contrastarlas con las tuyas para fortalecer tu pensamiento a través del diálogo y la reflexión. Se trata de tener el coraje intelectual de cuestionar lo que piensas, de ponerlo a prueba frente a otras perspectivas. A veces reafirmarás tus convicciones, otras veces las matizarás, y en ocasiones las transformarás en algo totalmente distinto, incluso puede que las termines desechando por completo.

Una sociedad madura no se construye sobre el eco de una sola voz, sino sobre la armonía disonante de muchas voces.

Cuando intentas convencer sin conocer ni comprender otras posturas, reduces el diálogo a un monólogo. Porque si solo hablas con quienes piensan como tú, no estás debatiendo, estás hablándole a un espejo que te retroalimenta reflejando tus propias ideas como si fueran universales. Eso no es debate, es autosatisfacción emocional. Y una persona que no se atreve a confrontar sus ideas para que sean cuestionadas, está condenada al estancamiento intelectual.

La información es alimento para la consciencia

En el foro libre de elhacker.net se han llegado a abordar temas que abarcan desde la política y la economía hasta cuestiones espirituales y filosóficas. Se ha debatido sobre religión, conflictos bélicos, fenómenos paranormales y experiencias sobrenaturales, ciencia, física, astronomía, la psicología, la ufología, y más.

Aunque, lo cierto es que aquella era dorada llegó a su fin, pues fue necesario establecer límites y censuras sobre los temas que podían tratarse, por que muchos debates acababan en desencuentros y discusiones, no en intercambios respetuosos de información (y yo no estoy libre de culpa en algunos casos).

Pero cada uno de estos temas ha contribuído y seguirán contribuyendo a expandir los límites de nuestra percepción y comprensión del mundo, a través del debate. Aquí no solo compartimos información: compartimos preguntas, dudas, intuiciones y certezas que, juntas, nutren la consciencia individual y colectiva de cada uno de nosotros.

Este espacio, el foro libre, no busca imponer verdades absolutas, sino invitar al pensamiento, al cuestionamiento, al aprendizaje y al desaprendizaje. Porque el conocimiento, cuando se comparte y se discute con respeto, se convierte en una herramienta poderosa de evolución.

Así que, por favor, les pido que tengan en cuenta 'La esencia del debate' cuando abran o participen en un hilo destinado al debate.

Atentamente,
Elektro.



PD: He utilizado ChatGPT como apoyo para pulir ciertos fragmentos de esta redacción, incorporando algunas reflexiones y matices filosóficos aquí y allá, sencillamente por que la IA sabe expresarse de forma mucho más precisa y "académica" de lo que yo soy capaz. Sin embargo, aunque parte del contenido haya sido generado con ayuda de una IA, cada palabra refleja con fidelidad lo que quiero transmitir. El texto es este, y no otro diferente, porque expresa exactamente lo que deseo compartir. 👍

Cierro el hilo.
6  Foros Generales / Foro Libre / Tom Campbell: Teoría del todo en Mecánica Cuántica (la muerte, la vida y por qué existimos) en: 10 Julio 2025, 09:40 am
¡Hola! Quiero compartir con ustedes dos videos del físico Tom Campbell, autor de la trilogía de libros 'My Big TOE' (Theory of Everything) cuya visión me ha resultado particularmente reveladora, puesto que me han ayudado a compaginar y darle explicación lógica y coherente a algún que otro interrogante fundamental que me faltaba por resolver dentro de mi creencia personal sobre eso que muchos llaman "Dios", que como podrán intuir con este hilo es una idea que se aleja bastante de la religión...

No voy a resumir su teoría ni adelantarles nada, quien tenga verdadero interés en conocer esta teoría del todo debería tomarse el tiempo necesario para visualizar y escuchar el video completo, desde el principio hasta el final. Esa es la única forma para poder aprender y opinar sobre este asunto con propiedad.

Tom Campbell: Un experto en mecánica cuántica explica la muerte, la vida y por qué existimos:


Tom Campbell: Charla informal en Atlanta, 22 nov. 2017 (subtitulada al español):




¿Que les parece a ustedes lo que dice esta persona en el video?.



También quiero recomendarles, sin lugar a dudas, el primer libro de la trilogía 'Conversaciones con Dios' (Conversations with God), del autor Neale Donald Walsch. Este libro en esencia viene a exponer prácticamente lo mismo que expone Tom Campbell, pero desde una perspectiva narrativa/conversacional y sin enfoque científico. No es que sea una lectura interpretativa ni espiritual, por que todo lo escrito en este libro son, literalmente, preguntas y respuestas, explicaciones que no dejan lugar a dudas para la libre interpretación en sus afirmaciones, pero se utiliza un lenguaje y un enfoque distintos, con diferentes términos y diferentes matices, aunque, en esencia, llegan a conclusiones  o afirmaciones extremadamente similares....

En realidad, el libro 'Conversaciones con Dios' se presenta literalmente como lo que su título indica: la transcripción de varias conversaciones directas con Dios. Y créanme, está tan bien redactado el texto (y en la traducción hispana también) que da la sensación de que esto realmente solo lo ha podido escribir Dios, eso o el escritor con la mejor habilidad del planeta Tierra capaz de transmitir ideas al lector de la forma más precisa inimaginable. Es sorprendente en ese sentido. Cuando ya llevamos unas pocas páginas de lectura, en la supuesta transcripción se dice textualmente que la información por escrito se te va a transmitir de tal forma que te va a resultar imposible albergar dudas de que es "Dios" quien lo ha escrito, y razón no le falta... la redacción en las supuestas respuestas de Dios es impresionantemente habilidosa, exquisita y precisa, insisto.

Sin embargo, creo que hay que leerlo desde una perspectiva más abierta y simbólica, entendiendo que más allá de si realmente se trata o no de una conversación literal con "Dios", el verdadero valor está en los conceptos que transmite el libro, en la profundidad de las ideas que expone y en cómo nos invita a replantearnos nuestras creencias para darle un significado lógico a eso que llaman "Dios". Porque a fin de cuentas, lo importante no es tanto quién lo dijo... sino lo que nos hace sentir, pensar y transformar.


( En España solo se distribuyó en edición física de bolsillo, con una letra en miniatura imposible de leer; Es preferible leerlo en PDF u otro formato digital. )



Y hablando de transformar (como una metáfora de evolucionar), también les recomendaría las ideas y conceptos expuestos por George Ivanovich Gurdjieff en sus doctrinas filosóficas recogidas por su alumno Peter Demianovich Ouspensky en el libro 'El Cuarto Camino' (The Fourth Way), aunque aquí ya no me atrevería a afirmar rotundamente que en esencia también se está exponiendo lo mismo que expone Tom Campbell y Neale Donald Walsch, pero yo le encuentro suficientes similitudes...


 
Sin embargo, debo advertirles que este libro 'El Cuarto Camino' es muy interpretativo y, aunque tiene mucha matemática con la que se intenta darle explicación a varios fenómenos, claramente se intenta que sea el lector quien encuentre las respuestas por si mismo a los interrogantes más existenciales. A mi me parece todo más subjetivo, con una opacidad de un ligero tinte misticista si cabe; No se llegan a entender muchas de las ideas. Eso no significa que sea un mal libro, si lo recomiendo es por algo. Y calificarlo "de tinte misticista" tal vez sea algo injusto, tal vez la palabra correcta simplemente sea filosófico o hermético, como lo es también 'El Kybalión' (de autor anónimo y supuestamente vinculado con las enseñanzas de Hermes Trismegisto), otro libro con el que cierta persona me recomendó empezar a leer hace ya bastantes años (y siempre se lo agradeceré ;D)...


( Este libro es considerado por la mayoría como un libro esotérico, pero no tiene por qué: depende del enfoque que le de el lector al leer su contenido. )

En definitiva, para mi la clave en esta búsqueda de la verdad sobre el significado de Dios, el universo y nuestra existencia, está en juntar todas estas piezas de ideas aparentemente muy similares y conexas entre sí, de estos los libros y autores que he mencionado en este hilo, e intentar aplicar la teoría de Tom Campbell, con lo que todo lo demás cobraría un sentido casi, casi definitivo.

PD: puedo estar equivocado, al fin y al cabo "YO SOY" un ser humano.

Atentamente,
Elektro.
7  Foros Generales / Sugerencias y dudas sobre el Foro / Hilo borrado (¿por error?): sugar daddy en: 30 Junio 2025, 00:23 am
¡Hola! Abro esta cuestión por que no puedo usar el botón 'reportar' en un tema de la papelera.

Me he fijado que se ha "reciclado" un hilo titulado "8.500 universitarias buscan su 'sugar daddy': "No es prostitución, es dar un braguetazo"...

Me pregunto si esto es un error, por que si es algo deliberado honestamente no lo entendería, ya que considero que solamente se debería haber eliminado el último comentario, es decir el del bot, pero no el hilo entero, y menos todavía siendo del año 2021 (incapaz de generar polémica ni impacto negativo en el foro a estas alturas) y de un grandísimo colaborador y persona como es Machacador, quien no se merece un hilo borrado sin motivo razonable.

Por mi parte cierro este hilo para evitar que se malinterpreten mis intenciones, sin embargo, esta vez sí, lo dejo deliberadamente sin "reciclar" (no veo la necesidad de hacerlo, a menos que alguien esté interesado en tapar algo impidiendo que los usuarios como yo podamos hacer preguntas en público que puedan cuestionar ciertas acciones que nos parezcan incorrectas, aunque lo hagamos por no conocer los detalles exactos de esa decisión).

No sé si compartirán mi opinión. Bueno, por eso estoy escribiendo esto, por que a mi me parece un error, ya sea un error humano o un error de criterio. Solo lo comento por si acaso realmente es un error humano; No voy a intentar combatir la decisión de un moderador/staff, aunque me parecería muy mal, ya que el hilo en vez de eliminarlo se podría haber bloqueado, eso sería la opción menos perjudicial y sería igual de efectivo: no habría más bots "rastreando" y respondiendo a ese hilo en el futuro.

Atentamente,
Elektro.
8  Foros Generales / Dudas Generales / Consulta sobre bloqueo de usuarios en Telegram en: 27 Junio 2025, 01:50 am
Tengo una duda general sobre Telegram.

Si estoy chateando con alguien y después de unos 30 minutos reviso el chat y me aparece el mensaje "Visto por última vez hace mucho tiempo" como se muestra en esta imagen:



Y además ya no me aparece su foto de perfil / avatar…

¿Eso significa que esa persona me ha bloqueado?.

Pero a esa persona yo todavía puedo enviarle mensajes, y Telegram no me muestra ninguna notificación que me indique que me han bloqueado. Por eso me resulta confuso, y quería saber si realmente eso es señal de un bloqueo.

Quizás la pregunta que en realidad debería hacer sea: ¿cuando una persona te bloquea en Telegram puedes seguir enviándole mensajes? (aunque obviamente, en caso de ser así, esos mensajes no los recibirá)

Atentamente,
Elektro.
9  Programación / Scripting / [APORTE] [Bash] Instalador completo de compatibilidad Windows en Linux (con Wine) en: 8 Junio 2025, 13:12 pm
Instalador automatizado de compatibilidad Windows en Linux (con Wine)

Este script Bash automatiza completamente la habilitación e integración de compatibilidad con aplicaciones de Windows en sistemas Linux, incluyendo:

  • Instalación de Wine y sus dependencias.
  • Interfaz gráfica para Wine.
  • Asociación automática de archivos .exe en el gestor de archivos.
  • Generación automática de iconos (miniaturas) para ejecutables de Windows.
  • Fuentes TrueType de Windows.
  • Runtimes de Visual Basic,Visual C++, .NET Framework y .NET 6.0+
  • DirectX y codecs de video.
  • Librerías DLL específicas y componentes auxiliares.
  • Acceso a la consola CMD de Windows

Con todos estos componentes instalados he podido ejecutar bajo Linux casi cualquier aplicación para Windows, la mayoría modernas, así como también video juegos antiguos que no son de Steam.

No van a funcionar absolutamente todas las aplicaciones de Windows, ya que por lo poco que sé Wine no soporta .NET Framework 4.x en su totalidad, y alguna aplicación puede solicitar librerías específicas que habría que copiar directamente desde una instalación de Windows a Linux, y luego registrar los archivos con regsvr32.exe en caso de ser necesario (y suerte intentando hacer que eso funcione), pero la mayoría de software que he probado ha funcionado correctamente sin hacer nada especial, incluyendo software compilado bajo .NET 8 y .NET 9 que apuntan a Windows.

El script ha sido probado en Xubuntu (XFCE) y Kubuntu (KDE).


(hacer click en la imagen para agrandar)

Cabe mencionar que, y según creo tener entendido, 'sudo' se puede configurar para que siempre pida contraseña cada vez que se invoca durante la misma sesión de la terminal, o que después de 1 minuto / un tiempo configurable se vuelva a pedir contraseña la próxima vez que se invoque, así que en esos casos pues el script no realizará un procedimiento totalmente automatizado...



Tras la instalación de todos estos componentes el script generará varios accesos directos y lanzadores en el escritorio de nuestro gestor de archivos (no sé si funcionará para todos) para abrir los componentes de Wine, la carpeta del disco "C:\" de Windows y la CMD, además, en caso de tener instalado el gestor de archivos Thunar también se generarán unas acciones personalizadas en el menú contextual del gestor para ejecutar un programa con Wine y también para abrir la CMD en el directorio actual o abrir el directorio en "Windows Explorer".

Todo esto con la finalidad de mejorar un poquito más la experiencia de integración con Wine.


(hacer click en la imagen para agrandar)

Nota: La instalación podría demorar entre 30~60 minutos aprox. dependiendo del hardware y la velocidad de conexión a Internet. En mi caso, usando una máquina virtual con el sistema operativo Xubuntu, con 6 GB de RAM y 4 núcleos, tarda unos 40 minutos. Y se requiere de aprox. 10 GB de espacio libre en disco para los archivos a instalar, además de archivos temporales adicionales que se descargan durante el procedimiento de instalación; El script elimina estos archivos temporales después de completar la instalación y el espacio total utilizado se reduce aprox. 2,50 GB, dejando unos 7,50 GB utilizados.

Tengan en cuenta que escribir este script me ha supuesto muchas horas de esfuerzo e investigación ya que no tengo experiencia con Bash (este es de mis primeros scripts) ni con Wine ni con Linux en general, así que he recurrido a ChatGPT en muchas ocasiones para armar hasta la línea de código más básica, a base de mucho ensayo y error (20% errores míos, 80% errores de esta IA inútil), y también horas de tediosa experimentación, ya que instalar un componente o una librería "equivocada" con winetricks supone que Wine se vaya al carajo y no funcione ningún ejecutable de Windows, lo cual me ha ocurrido varias veces... y vuelta a empezar de cero.

¿Mi motivación para escribir este script?, el simple capricho de poder usar WinRAR y otras aplicaciones para Windows que considero esenciales y sin un equivalente digno para Linux, sumado a mi cabezonería y el tiempo libre para experimentar.



El script de instalación:

Wine_Install.sh

Código:
#!/bin/bash
set -e

###############################################################################
#                              CONFIGURATION                                  #
###############################################################################

DESKTOP_DIR="$(xdg-user-dir DESKTOP)"

###############################################################################
#                                  FUNCTIONS                                  #
###############################################################################

create_desktop_launcher() {
    local EXEC_PATH="$1"               # Path to executable or URL (required)
    local APP_NAME="$2"                # Display name of the app (required)
    local COMMENT="${3:-}"             # Comment/description (optional)
    local ICON_PATH="${4:-}"           # Path to icon file or URL (optional)
    local TERMINAL="${5:-false}"       # true or false to run in terminal (optional, default false)
    local TYPE="${6:-Application}"     # Desktop entry type (Application, Link, etc.) (optional)
    local CATEGORIES="${7:-Utility;}"  # Categories for the launcher (optional)
    local ARGS="${8:-}"                # Arguments for the executable (optional)
    local DESKTOP_FILE="${9:-$DESKTOP_DIR/$APP_NAME.desktop}" # Path to .desktop file (optional)

    # Validate required params
    if [ -z "$EXEC_PATH" ] || [ -z "$APP_NAME" ]; then
        echo "❌ Usage: create_desktop_launcher <exec_path> <app_name> [comment] [icon_path] [terminal:true|false] [type] [categories] [args] [desktop_file]"
        return 1
    fi

    # Handle icon: if URL, download it to ~/.local/share/icons/<app_name>.png
    if [[ "$ICON_PATH" == http* ]]; then
        local ICON_DIR="$HOME/.local/share/icons"
        mkdir -p "$ICON_DIR"
        local SAFE_APP_NAME=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '_')
        local LOCAL_ICON_PATH="$ICON_DIR/$SAFE_APP_NAME.png"

        if [ ! -f "$LOCAL_ICON_PATH" ]; then
            # echo "⬇️ Downloading icon from URL: $ICON_PATH"
            if command -v wget > /dev/null 2>&1; then
                wget -qO "$LOCAL_ICON_PATH" "$ICON_PATH"
            elif command -v curl > /dev/null 2>&1; then
                curl -sL "$ICON_PATH" -o "$LOCAL_ICON_PATH"
            else
                echo "❌ Neither wget nor curl found. Cannot download icon for desktop launcher."
                # return 1
            fi

            if [ ! -f "$LOCAL_ICON_PATH" ]; then
                echo "❌ Icon download failed for desktop launcher. Check your internet connection or URL."
                # return 1
            fi
        fi
        ICON_PATH="$LOCAL_ICON_PATH"
    fi

    # Write .desktop file
    {
        echo "[Desktop Entry]"
        echo "Name=$APP_NAME"
        [ -n "$COMMENT" ] && echo "Comment=$COMMENT"
        echo "Exec=$EXEC_PATH $ARGS"
        [ -n "$ICON_PATH" ] && echo "Icon=$ICON_PATH"
        echo "Terminal=$TERMINAL"
        echo "Type=$TYPE"
        echo "Categories=$CATEGORIES"
    } > "$DESKTOP_FILE"
}

create_thunar_custom_action() {
  local UCA_FILE="$HOME/.config/Thunar/uca.xml"
  local UNIQUE_ID="$1"
  local ICON="$2"
  local NAME="$3"
  local SUBMENU="$4"
  local COMMAND="$5"
  local DESCRIPTION="$6"
  local RANGE="$7"
  local PATTERNS="$8"
  local EXTRA_TAGS="${9}"

  if [ ! -f "$UCA_FILE" ]; then
    sudo mkdir -p "$(dirname "$UCA_FILE")"
    echo '<?xml version="1.0" encoding="UTF-8"?>
<actions>
</actions>' > "$UCA_FILE"
    # echo "✨ Created new uca.xml configuration file."
  fi

  if grep -q "<unique-id>$UNIQUE_ID</unique-id>" "$UCA_FILE"; then
    # echo "⚠️ Custom Action '$NAME' already exists in Thunar."
    return
  fi

  local ACTION_BLOCK="  <action>
    <icon>$ICON</icon>
    <name>$NAME</name>
    <submenu>$SUBMENU</submenu>
    <unique-id>$UNIQUE_ID</unique-id>
    <command>$COMMAND</command>
    <description>$DESCRIPTION</description>
    <range>$RANGE</range>
    <patterns>$PATTERNS</patterns>"
 
  if [ -n "$EXTRA_TAGS" ]; then
    ACTION_BLOCK="${ACTION_BLOCK}
    $EXTRA_TAGS"
  fi

  ACTION_BLOCK="${ACTION_BLOCK}
  </action>"

  awk -v block="$ACTION_BLOCK" '
    /<\/actions>/ {
      print block
    }
    { print }
  ' "$UCA_FILE" > "$UCA_FILE.tmp" && mv "$UCA_FILE.tmp" "$UCA_FILE"
  # echo "✅ Custom Action '$NAME' added successfully to Thunar."
}

###############################################################################
#                            MAIN SCRIPT EXECUTION                            #
###############################################################################

echo "ℹ️ INFO: This script will install Wine and required components and tools"
echo "          to integrate and run Windows applications on your Linux system."
echo ""
echo "⚠️ About 10 GB of free space is required to download and install all componentes."
echo "    The installation may take approximately 40~60 minutes."
echo
echo -n "Do you want to continue? [Y/N]: "
while true; do
    read -n1 confirm
    echo
    if [[ -z "$confirm" || "$confirm" =~ ^[Yy]$ ]]; then
        break
    elif [[ "$confirm" =~ ^[Nn]$ ]]; then
        echo "❌ Operation cancelled."
        exit 1
    else
        echo "⚠️ Invalid option. Please press Y or N."
    fi
done
clear

echo "🛠️ Adding support for 32-bit architecture and packages (required to install wine32)..."
sudo dpkg --add-architecture i386

echo -e "\n🔄 Updating package list to fetch latest info..."
sudo apt-get update

echo -e "\n🍷 Installing Wine (wine32, wine64), required to run Windows apps on Linux..."
# https://www.winehq.org/
sudo apt-get install --install-recommends -y wine wine32 wine64 libwine fonts-wine

echo -e "\n🍷 Installing Qt GUI for Wine..."
# https://github.com/brezerk/q4wine
sudo apt-get install --install-recommends -y q4wine

echo -e "\n🍷 Installing Winbind, required for Windows user authentication integration with Wine..."
# https://www.samba.org/samba/docs/current/man-html/winbindd.8.html
sudo apt-get install -y winbind

echo -e "\n🍷 Installing WineTricks for automatic installation of Windows runtimes and libraries with Wine..."
# https://github.com/Winetricks/winetricks
sudo apt-get install --install-recommends -y winetricks
# Installs latest winetricks script, allowing later dotnetdesktop8 and dotnetdesktop9 runtime installation.
# https://github.com/Winetricks/winetricks/issues/2178#issuecomment-2299877869
yes | sudo winetricks --self-update # Use sudo, it requires @root permissions to self update.

echo -e "\n🍷 Installing wine-binfmt, which causes Wine to be invoked automatically whenever a Windows .exe file is to be launched..."
# https://packages.debian.org/sid/wine-binfmt
# https://binfmt-support.nongnu.org/
sudo apt-get install -y wine-binfmt binfmt-support

if command -v thunar >/dev/null 2>&1; then
    echo -e "\n📦 Installing Tumbler and its plugins to enable thumbnail generation for various file types in Thunar file manager."
    # https://docs.xfce.org/xfce/thunar/4.14/tumbler
    # https://packages.debian.org/sid/xfce/tumbler-plugins-extra
    sudo apt-get install -y tumbler tumbler-plugins-extra
fi

echo -e "\n📦 Installing icoextract-thumbnailer (exe-thumbnailer), which generates thumbnail previews for Windows .exe files in your file manager..."
# https://github.com/jlu5/icoextract?tab=readme-ov-file#installing-from-source
sudo apt-get install --install-recommends -y icoextract-thumbnailer

echo -e "\n🆕 Creating file: '/usr/local/share/thumbnailers/exe-thumbnailer.thumbnailer' with thumbnail previews configuration for Windows PE files..."
# https://github.com/jlu5/icoextract/blob/master/exe-thumbnailer.thumbnailer
sudo mkdir -p "/usr/local/share/thumbnailers/" && {
  if [ ! -f "/usr/local/share/thumbnailers/exe-thumbnailer.thumbnailer" ]; then
    sudo tee "/usr/local/share/thumbnailers/exe-thumbnailer.thumbnailer" > /dev/null << EOF
[Thumbnailer Entry]
Exec=exe-thumbnailer -v -s %s %i %o
MimeType=application/x-ms-dos-executable;application/x-dosexec;application/x-msdownload;application/vnd.microsoft.portable-executable
EOF
  fi
}

echo -e "\n🧹 Removing existing Wine configuration in '~/.wine' (if any)..."
rm -rf ~/.wine

echo -e "\n🆕 Creating new Wine configuration with Windows 10 settings..."
sudo WINEPREFIX="/root/.wine" wine-stable winecfg -v win10
WINEPREFIX=~/.wine wine-stable winecfg -v win10

echo -e "\n🅰️ Installing Microsoft Windows fonts for better app compatibility..."
winetricks --unattended corefonts calibri cambria consolas uff

echo -e "\n⚙️ Installing Microsoft Windows runtimes..."
winetricks --unattended vb6run
winetricks --unattended vcrun6sp6 vcrun2005 vcrun2008 vcrun2010 vcrun2012 vcrun2013
winetricks --unattended --force vcrun2022 # Force installation due installer file checksum missmatch.
{
  winetricks --unattended dotnet48
  # These registry keys forces all .NET Framework assemblies to run on the latest installed CLR (i.e., dotnet48)
  # It also solves issue: https://bugs.winehq.org/show_bug.cgi?id=41727#c5
  wine reg add "HKLM\\Software\\Microsoft\\.NETFramework" /v "OnlyUseLatestCLR" /t "REG_DWORD" /d "0001" /f
  wine reg add "HKLM\\Software\\WOW6432Node\\Microsoft\\.NetFramework" /v "OnlyUseLatestCLR" /t "REG_DWORD" /d "0001" /f
}
winetricks --unattended dotnetdesktop6 dotnetdesktop7
 # .NET 8 and .NET 9 are available only from latest winetricks release which we got with 'winetricks --self-update' command.
winetricks --unattended dotnetdesktop8 dotnetdesktop9

echo -e "\n🎥 Installing codecs for media playback (also useful for Windows video games)..."
winetricks --unattended cinepak directshow ffdshow l3codecx openal

echo -e "\n📚 Installing essential Microsoft Windows libraries..."
winetricks --unattended cabinet crypt32 dbghelp gdiplus hid iertutil msacm32 msxml6 \
                        ole32 oleaut32 setupapi uiribbon urlmon webio wininet

echo -e "\n🖥️ Installing DirectX runtimes and APIs for graphics and gaming..."
winetricks --unattended d3dx9 d3dx10 dxdiag d3drm d3dxof dx8vb dxtrans \
                        devenum dinput8 directmusic directplay mdx sdl

echo -e "\n🖥️ Installing Vulkan APIs for advanced graphics support..."
winetricks --unattended dxvk vkd3d

echo -e "\n💾 Installing Microsoft Windows CMD..."
winetricks --unattended cmd

echo -e "\n🔄 Restoring Wine configuration to Windows 10 settings..."
wine winecfg -v win10

echo -e "\n🧹 Cleaning winetricks cache folder in '~/.cache/winetricks'..."
rm -rf ~/.cache/winetricks/*

echo -e "\n🚀 Creating desktop link: 'Wine (C:\)'..."
if command -v dolphin >/dev/null 2>&1; then
    WINE_DIR_ICON="folder-windows-symbolic"
else
    WINE_DIR_ICON="wine"
fi
printf '%s\n' \
"[Desktop Entry]" \
"Type=Link" \
'Name=Wine (C:\\)' \
"Icon=$WINE_DIR_ICON" \
"URL=file://$HOME/.wine/drive_c" \
> "$DESKTOP_DIR/wine-open-drive_c.desktop"

echo -e "\n🚀 Creating desktop launcher: 'Wine Configuration'..."
create_desktop_launcher \
    "wine-stable winecfg" \
    "Wine Configuration" \
    "Launch Wine Configuration" \
    "wine-stable" \
    "false" \
    "Application" \
    "Utility;" \
    "" \
    "$DESKTOP_DIR/wine-configuration.desktop"

echo -e "\n🚀 Creating desktop launcher: 'Qt GUI for Wine'..."
create_desktop_launcher \
    "q4wine" \
    "Qt GUI for Wine" \
    "Launch Qt GUI for Wine" \
    "q4wine" \
    "false" \
    "Application" \
    "Utility;" \
    "" \
    "$DESKTOP_DIR/qt-gui-for-wine.desktop"

echo -e "\n🚀 Creating desktop launcher: 'Winetricks'..."
create_desktop_launcher \
    "winetricks --gui" \
    "Winetricks" \
    "Launch Winetricks with GUI" \
    "winetricks" \
    "false" \
    "Application" \
    "Utility;" \
    "" \
    "$DESKTOP_DIR/winetricks.desktop"

if command -v thunar >/dev/null 2>&1; then
    CMD_EXEC="xfce4-terminal --title=\"CMD\" --color-bg=#000000 --color-text=#FFFFFF -e 'sudo wine-stable cmd.exe'"
    CMD_RUN_IN_TERMINAL=true
elif command -v dolphin >/dev/null 2>&1; then
    CMD_EXEC="konsole --hold -e sudo wine-stable cmd.exe"
    CMD_RUN_IN_TERMINAL=false
fi
if [[ -n "$CMD_EXEC" ]]; then
    echo -e "\n🚀 Creating desktop launcher: 'CMD'..."
    create_desktop_launcher \
        "$CMD_EXEC" \
        "CMD" \
        "Launch Microsoft Windows CMD as root" \
        "https://upload.wikimedia.org/wikipedia/commons/7/7b/CMD-Icon_%28small%29.png" \
        $CMD_RUN_IN_TERMINAL \
        "Application" \
        "Utility;" \
        "" \
        "$DESKTOP_DIR/cmd.desktop"
fi

if command -v thunar >/dev/null 2>&1; then

    echo -e "\n🚀 Creating Thunar custom action: 'Run with Wine'..."
    create_thunar_custom_action \
        "wine-run-with-wine" \
        "wine" \
        "Run with Wine" \
        "" \
        "wine-stable %f" \
        "Run a Microsoft Windows executable with Wine" \
        "*" \
        "*.exe" \
        "<other-files/>"

    echo -e "\n🚀 Creating Thunar custom action: 'Run with Wine (Terminal)'..."
    create_thunar_custom_action \
        "wine-run-with-wine-terminal" \
        "wine" \
        "Run with Wine (Terminal)" \
        "" \
        "xfce4-terminal --hold -e &quot;wine-stable %f&quot;" \
        "Run a Microsoft Windows executable with Wine (Terminal)" \
        "*" \
        "*.exe" \
        "<other-files/>"

    echo -e "\n🚀 Creating Thunar custom action: 'Open CMD here'..."
    create_thunar_custom_action \
        "wine-open-cmd-here" \
        "cmd" \
        "Open CMD here" \
        "" \
        "sudo xfce4-terminal --title=&quot;CMD&quot; --color-bg=#000000 --color-text=#FFFFFF -e &quot;wine start /wait /b /d %f cmd&quot;" \
        "Open a Windows CMD in the current directory" \
        "*" \
        "*" \
        "<directories/>"

    echo -e "\n🚀 Creating Thunar custom action: 'Open in Explorer (Wine)'..."
    create_thunar_custom_action \
        "wine-open-in-explorer" \
        "folder" \
        "Open in Explorer (Wine)" \
        "" \
        "wine-stable explorer %f" \
        "Open the current directory in Explorer (Wine)" \
        "*" \
        "*" \
        "<directories/>"

fi

echo -n -e "\n🏁 Operation completed."
echo
read -n1 -r -s -p "Press any key to finish..."
echo
exit



Adicionalmente, recomiendo utilizar estos comandos para instalar los runtimes de .NET 8 y 9 en Linux:

Código:
sudo apt install -y dotnet-runtime-8.0
sudo apt install -y dotnet-runtime-9.0

He preferido no incluir la instalación de estos runtimes en el script por que son parte del soporte nativo en Linux para aplicaciones .NET 8 y .NET 9 que apuntan a Linux; No tienen relación ni influencia sobre Wine. El script ya instala los respectivos runtimes de .NET 8 y 9 dentro de su entorno para poder ejecutar con Wine los binarios de .NET 8 y 9 que apuntan a Windows.



Por último, comparto este script para revertir la instalación de Wine y sus componentes:

Wine_Uninstall.sh
Código:
#!/bin/bash

###############################################################################
#                            MAIN SCRIPT EXECUTION                            #
###############################################################################

echo "⚠️ WARNING: This script will uninstall Wine and related components, directories, caches and configurations COMPLETELY."
echo
echo -n "Do you want to continue? [Y/N]: "
while true; do
    read -n1 confirm
    echo
    if [[ -z "$confirm" || "$confirm" =~ ^[Yy]$ ]]; then
        break
    elif [[ "$confirm" =~ ^[Nn]$ ]]; then
        echo "❌ Operation cancelled."
        exit 1
    else
        echo "⚠️ Invalid option. Please press Y or N."
    fi
done
clear

# Remove wine directory, including "drive_c" and its contents.
WINEPREFIX="/root/.wine" winetricks --unattended annihilate
WINEPREFIX="$HOME/.wine" winetricks --unattended annihilate

sudo rm -rf "/root/.wine"
rm -rf "$HOME/.wine"
rm -rf "$HOME/.cache/winetricks"

# Remove packages that are part of Wine or strictly related to.
sudo apt-get remove --purge -y wine wine32 wine64 libwine fonts-wine \
                               q4wine winetricks wine-binfmt

# Remove auxiliary packages that are not strictly tied to Wine but are "safe" to remove.
sudo apt-get remove --purge -y binfmt-support winbind \
                               samba-common samba-common-bin samba-dsdb-modules

# Additional package cleanup.
sudo apt-get autoremove -y
sudo apt-get clean

echo -e "\n🏁 Operation completed."
read -n1 -r -s -p "Press any key to finish..."
echo
exit 0

⚠️ Este script de desinstalación eliminará por completo el directorio de Windows (~/.wine) y todo lo que haya en él, sin embargo, no revierte por completo todos los paquetes instalados previamente por el script del instalador de Wine, en parte por que me parece peligroso forzar la desinstalación de ciertos paquetes relacionados con gtk que se instalaron por parte del paquete q4wine con el parámetro --install-recommends, entre otros paquetes recomendados que se instalan relacionados con Python, ya que todo eso podría depender de otros paquetes que el usuario tuviera instalado anteriormente, así que solo desinstala los paquetes estrictamente relacionados con Wine y los que considero que prácticamente nadie estará utilizando para otras cosas: binfmt-support, winbind, samba*

Revisen los paquetes que este script desinstala antes de usarlo. No me hago responsable.
10  Programación / Scripting / [APORTE] [Bash] (Linux) VMWare: Montar / Desmontar 'Shared Folders' en: 4 Junio 2025, 21:09 pm
Los siguientes dos scripts, desarrollados en el lenguaje Bash, y que se deben usar bajo un sistema operativo Linux en una máquina virtual de VMWare (aunque se pueden modificar para Virtual Box), sirven como atajo para montar y desmontar las "carpetas compartidas" (shared folders) de forma sencilla.

Utilizar estos scripts nos ahorra un valioso tiempo al no tener que usar comandos específicos en la distro de Linux para llevar a cabo el montaje de las carpetas compartidas de VMWare y de forma repetitiva (ya que el montaje no es persistente).

La idea tras esta simple herramienta es eso, ahorrar tiempo, y de esta manera, mediante el script de desmontaje, poder aislar la máquina virtual del sistema operativo anfitrión impidiendo el acceso a las carpetas compartidas, y volver a habilitar el acceso, tan solo haciendo dos clicks para ejecutar estos scripts.

 

Estos scripts son un equivalente de estos otros para Windows:

  — Tema: [APORTE] [VBS] VMWare: Mount / Unmount Shared Folders Network Drive

Sin embargo, en esta ocasión estos scripts que comparto están programados en el idioma español, e incluyen emojis. Para ser honestos yo no tengo experiencia con Bash, así que básicamente lo ha hecho casi todo ChatGPT, yo solo le di unas pinceladas.

Mount_VMWare_Shared_Folders.sh
Código:
#!/bin/bash

# Carpeta a montar: VMWare Shared Folders (".host:/" = todas las carpetas)
# Destino: "/mnt/shared_folders"
MOUNT_SOURCE=".host:/"
MOUNT_DESTINATION="/mnt/shared_folders"

# =========================================
# Montar las carpetas compartidas de VMWare
# =========================================

# Crear /mnt/shared_folders si no existe
if [ ! -d "$MOUNT_DESTINATION" ]; then
    echo "📂 Creando carpeta $MOUNT_DESTINATION... 🔥"
    sudo mkdir -p "$MOUNT_DESTINATION"
fi

# Verificar si ya está montado en /mnt/shared_folders
if mountpoint -q "$MOUNT_DESTINATION"; then
    echo "⚠️ $MOUNT_DESTINATION ya está montado."
else
    echo "🔗 Montando carpeta compartida VMWare en $MOUNT_DESTINATION..."
    sudo vmhgfs-fuse "$MOUNT_SOURCE" "$MOUNT_DESTINATION" -o allow_other
    if [ $? -ne 0 ]; then
        echo "❌ Error montando la carpeta compartida. Abortando... 💔"
        exit 1
    fi
    echo "✅ Montaje exitoso de carpeta compartida VMWare en $MOUNT_DESTINATION."
fi

# ====================================================================
# Crear enlace simbólico de las carpetas compartidas, en el escritorio
# ====================================================================

# Detectar ruta del escritorio usando user-dirs.dirs (XDG estándar)
DESKTOP_DIR=""

# Leer archivo de configuración XDG si existe
if [ -f "$HOME/.config/user-dirs.dirs" ]; then
    DESKTOP_DIR=$(grep XDG_DESKTOP_DIR "$HOME/.config/user-dirs.dirs" | cut -d= -f2 | tr -d \")
fi

# Si no lo encontró o está vacío, probar las carpetas comunes
if [ -z "$DESKTOP_DIR" ]; then
    if [ -d "$HOME/Desktop" ]; then
        DESKTOP_DIR="$HOME/Desktop"
    elif [ -d "$HOME/Escritorio" ]; then
        DESKTOP_DIR="$HOME/Escritorio"
    else
        echo "⚠️ No se pudo detectar la ubicación de la carpeta Desktop / Escritorio. Abortando... 😢"
        exit 1
    fi
else
    # Eliminar $HOME si está en la ruta para expandirla bien
    DESKTOP_DIR="${DESKTOP_DIR/#\$HOME/$HOME}"
fi

# Definir origen y destino
LINK_DESTINATION="$DESKTOP_DIR/VMWare Shared Folders"

# Crear enlace simbólico solo si no existe ya.
if [ -L "$LINK_DESTINATION" ] || [ -e "$LINK_DESTINATION" ]; then
    echo "⚠️ El enlace o archivo '$LINK_DESTINATION' ya existe. No se creará un nuevo enlace."
else
    ln -s "$MOUNT_DESTINATION" "$LINK_DESTINATION"
    echo "✅ Enlace simbólico creado en: $LINK_DESTINATION 🎉"
fi

echo -e "\n🏁 Operación finalizada."

Dismount_VMWare_Shared_Folders.sh
Código:
#!/bin/bash

# Ruta de montaje de las carpetas compartidas de VMware
MOUNT_DESTINATION="/mnt/shared_folders"

echo "🔍 Verificando si está montado $MOUNT_DESTINATION..."

if mountpoint -q "$MOUNT_DESTINATION"; then
    echo "📤 Desmontando carpeta compartida de VMware de: $MOUNT_DESTINATION..."
    sudo umount "$MOUNT_DESTINATION"
    if [ $? -eq 0 ]; then
        echo "✅ Carpeta compartida desmontada correctamente. 🎉"
    else
        echo "❌ Error al desmontar $MOUNT_DESTINATION. ¿Está en uso? 💔"
        exit 1
    fi
else
    echo "⚠️ $MOUNT_DESTINATION no está montado actualmente."
    if [ -d "$MOUNT_DESTINATION" ]; then
        echo "ℹ️ La carpeta existe, pero no contiene un punto de montaje activo."
    else
        echo "🚫 La carpeta $MOUNT_DESTINATION no existe."
    fi
fi

echo -e "\n🏁 Operación finalizada."

PD: parece que SMF no se lleva bien con los emojis, pierden el formato al usar la etiqueta "Bash" en el bloque de código.
Páginas: [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... 107
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines