Full Code of g0tmi1k/exe2hex for AI

master e563b353306a cached
3 files
39.7 KB
11.3k tokens
25 symbols
1 requests
Download .txt
Repository: g0tmi1k/exe2hex
Branch: master
Commit: e563b353306a
Files: 3
Total size: 39.7 KB

Directory structure:
gitextract_yc0xu9k8/

├── LICENSE
├── README.md
└── exe2hex.py

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 g0tmi1k

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
# exe2hex

Inline file transfer using in-built Windows tools (`DEBUG.exe` or PowerShell).

<p align="center">
  <img src="http://i.imgur.com/n6Op45O.png" alt="exe2hex logo"/>
</p>

- - -

### Overview

exe2hex encodes an executable binary file into ASCII text format.

The result then can be transferred to the target machine (It is much easier to echo a ASCII file than binary data).

Upon executing exe2hex's output file, the original program is restored by using `DEBUG.exe` or PowerShell (which are pre-installed by default on Windows).

Can be automated by using either the in-built Telnet or WinEXE options in exe2hex to transfer the file over to the target machine, else the output can manually be inserted.

```Binary EXE -> ASCII Text -> *Transfer* -> Binary EXE```

![](https://i.imgur.com/vAmiyj9.png)

- - -

### Quick Guide

 + Input using a file (`-x /path/to/binary-program.exe`) or STDIN (`-s`)
 + Output to BATch (`-b file.bat`) and/or PoSH (`-p powershell.cmd`)

#### Example Usage

**Create BATch & PowerShell files**:
```bash
$ python3 exe2hex.py -x /usr/share/windows-binaries/sbd.exe
[*] exe2hex v1.5
[i] Outputting to /root/sbd.bat (BATch) and /root/sbd.cmd (PoSh)
[+] Successfully wrote (BATch) /root/sbd.bat
[+] Successfully wrote (PoSh) /root/sbd.cmd
$
```

**Compress the file before creating a BATch file**:
```bash
$ ./exe2hex.py -x /usr/share/windows-binaries/nc.exe -b /var/www/html/nc.txt -cc
[*] exe2hex v1.5
[i] Attempting to clone and compress
[i] Creating temporary file /tmp/tmpft9tmm_i
[+] Compression (strip) was successful! (0.0% saved)
[+] Compression (UPX) was successful! (50.9% saved)
[+] Successfully wrote (BATch) /var/www/html/nc.txt
$
```

**Use STDIN to create BATch & PowerShell files**:
```bash
$ cat /usr/share/windows-binaries/whoami.exe | python3 exe2hex.py -s -b debug.bat -p ps.cmd
[*] exe2hex v1.5
[i] Reading from STDIN
[+] Successfully wrote (BATch) /root/debug.bat
[+] Successfully wrote (PoSh) /root/ps.cmd
$
```

#### Help

```bash
$ python3 exe2hex.py
[*] exe2hex v1.5

Encodes an executable binary file into ASCII text format
Restore using DEBUG.exe (BATch - x86) or PowerShell (PoSh - x86/x64)

Quick Guide:
 + Input binary file with -s or -x
 + Output with -b and/or -p
Example:
 $ /usr/bin/exe2hex -x /usr/share/windows-binaries/sbd.exe
 $ /usr/bin/exe2hex -x /usr/share/windows-binaries/nc.exe -b /var/www/html/nc.txt -cc
 $ cat /usr/share/windows-binaries/whoami.exe | /usr/bin/exe2hex -s -b debug.bat -p ps.cmd

--- --- --- --- --- --- --- --- --- --- --- --- --- --- ---

Usage: exe2hex [options]

Options:
  -h, --help  show this help message and exit
  -x EXE      The EXE binary file to convert
  -s          Read from STDIN
  -b BAT      BAT output file (DEBUG.exe method - x86)
  -p POSH     PoSh output file (PowerShell method - x86/x64)
  -e          URL encode the output
  -r TEXT     pRefix - text to add before the command on each line
  -f TEXT     suFfix - text to add after the command on each line
  -l INT      Maximum HEX values per line
  -c          Clones and compress the file before converting (-cc for higher
              compression)
  -t          Create a Expect file, to automate to a Telnet session.
  -w          Create a Expect file, to automate to a WinEXE session.
  -v          Enable verbose mode
$
```

- - -

### Methods/OS Support

+ **`DEBUG.exe` (BATch mode - `-b`)**
  + Supports x86 OSs (No x64 support).
  + Useful for legacy versions of Windows (e.g. Windows XP/Windows 2000).
    + Pre-installed by default. Works out of the box.
  + ~~Limitation of 64k file size for binary programs.~~ Creates multiple parts and joins with `copy /b` so this is not an issue anymore!
+ **PowerShell (PoSh mode - `-p`)**
  + Supports both x86 & x64 OSs.
  + Aimed at more "recent" versions of Windows.
    + PowerShell was first integrated into core OS with Windows 7/Windows Server 2008 R2.
    + Windows XP SP2, Windows Server 2003 & Windows Vista requires PowerShell to be pre-installed.
  + This is **not** a `.ps1` file (pure PowerShell). It only calls PowerShell at the end.

- - -

### Features

**Primary purpose**: Convert a binary program into a ASCII HEX file which can be restored using in-built OS programs.

+ Work on old and new versions of Windows without requiring any 3rd party programs to be pre-installed.
+ Supports x86 & x64 OSs.
+ Can use DEBUG.exe or PowerShell to restore the file.
+ Able to compress the file before converting.
+ URL encode the output.
+ The option to add prefix and suffix text to each line.
+ Able to set a maximum HEX length per line.
+ Can use a binary file or pipe from standard input (`STDIN`).
+ Automate transfers over Telnet and/or WinEXE.


Note: This is nothing new. [The core idea (using DEBUG.exe for inline file transfer) has been around since 2003](https://www.blackhat.com/presentations/bh-asia-03/bh-asia-03-chong.pdf) _(if not earlier!)_.

- - -

### Telnet

When pasting a large amount of data (100+ lines) directly into a Telnet session, the results can be "unpredictable". Behaviours include lines being executed in a incorrect order or characters are just completely skipped.

A solution is to use "[Expect](http://expect.sourceforge.net/)" (which is an extension of [TCL](https://sourceforge.net/projects/tcl/)). Expect can be found in a most major Linux OSs repositories (`apt-get -y install expect` / `yum -y install expect` / `pacman -S expect`). Upon executing exe2hex's Telnet script, Expect will automate the Telnet login (based on the arguments used), look for a writeable folder (e.g. defaults to the system variable, `%TEMP%`) and then start inputting commands from exe2hex's output file, line by line one at a time. If required, the variables at the top of the Expect script can be manually edited (to use a different Telnet port, path, or command prompt).

An example of exe2hex's Telnet mode can be seen below:

```bash
$ python3 exe2hex.py -x /usr/share/windows-binaries/klogger.exe -b klogger.bat -t
[*] exe2hex v1.5
[+] Successfully wrote (BATch) /root/klogger.bat
[+] Successfully wrote (Expect) /root/klogger-bat-telnet
$
$ expect /root/klogger-bat-telnet
Usage: ./klogger-bat-telnet <ip> <username> <password>
$
$ /root/klogger-bat-telnet 192.168.103.148 winxp pass123

spawn telnet 192.168.103.148

Trying 192.168.103.148...
Connected to 192.168.103.148.
Escape character is '^]'.
Welcome to Microsoft Telnet Service

login: winxp
password:

*===============================================================
Welcome to Microsoft Telnet Server.
*===============================================================
C:\Documents and Settings\winxp>cd %TEMP%
C:\DOCUME~1\winxp\LOCALS~1\Temp>echo 418671.0>klogger.bat
418671.0E~1\winxp\LOCALS~1\Temp>type klogger.bat

C:\DOCUME~1\winxp\LOCALS~1\Temp>

[i] Writeable folder!

C:\DOCUME~1\winxp\LOCALS~1\Temp>del /F klogger.bat
Runs Debug, a program testing and editing tool.

DEBUG [[drive:][path]filename [testfile-parameters]]

  [drive:][path]filename  Specifies the file you want to test.
  testfile-parameters     Specifies command-line information required by
                          the file you want to test.

After Debug starts, type ? to display a list of debugging commands.

C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 1/382)
if NOT %ERRORLEVEL% == 0 echo &echo &echo &echo **** **** **** **** ****&echo *** Missing DEBUG.exe ***&echo **** **** **** **** ****&exit /b
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 2/382)
echo n klogger.0>klogger.hex
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 3/382)
echo e 0100>>klogger.hex
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 4/382)
echo 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 00 00 00 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00>>klogger.hex
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 5/382)
echo e 0180>>klogger.hex


...SNIP...

C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 376/382)
move /Y klogger.0 klogger.exe
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 377/382)
echo. >klogger.hex
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 378/382)
del /F /Q klogger.hex klogger.0
C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 379/382)
 Volume in drive C has no label.
 Volume Serial Number is 002C-A3B2

 Directory of C:\DOCUME~1\winxp\LOCALS~1\Temp

06/09/2017  10:19 AM            23,552 klogger.exe
               1 File(s)         23,552 bytes
               0 Dir(s)  40,501,571,584 bytes free

C:\DOCUME~1\winxp\LOCALS~1\Temp>C:\DOCUME~1\winxp\LOCALS~1\Temp>   (Progress: 380/382)


[i] Done

C:\DOCUME~1\winxp\LOCALS~1\Temp>
```


### WinEXE

Like the Telnet mode (`-t`), exe2hex can automate using winexe to transfer files across, inline, using expect:

```bash
$ python3 exe2hex.py -x /usr/share/windows-binaries/mbenum/mbenum.exe -p mbenum.cmd -w
[*] exe2hex v1.5
[+] Successfully wrote (PoSh) /root/mbenum.cmd
[+] Successfully wrote (Expect) /root/mbenum-posh-winexe
$
$ expect /root/mbenum-posh-winexe
Usage: ./mbenum-posh-winexe <ip> <username> <password>
$
$ ./mbenum-posh-winexe 192.168.103.147 win7 123456789

spawn winexe -U win7%123456789 //192.168.103.147 cmd.exe

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Windows\system32>cd %TEMP%
cd %TEMP%

C:\Windows\Temp>echo 656082.0>mbenum.cmd
echo 656082.0>mbenum.cmd

C:\Windows\Temp>type mbenum.cmd
type mbenum.cmd
656082.0

[i] Writeable folder!


C:\Windows\Temp>del /F mbenum.cmd
del /F mbenum.cmd

C:\Windows\Temp>echo|set /p="">mbenum.hex

echo|set /p="">mbenum.hex

C:\Windows\Temp>   (Progress: 1/388)

C:\Windows\Temp>echo|set /p="4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000e80000000e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000">>mbenum.hex

echo|set /p="4d5a90000300000004000000ffff0000b800000000000000400000000000000000000000000000000000000000000000000000000000000000000000e80000000e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000">>mbenum.hex

C:\Windows\Temp>   (Progress: 2/388)

C:\Windows\Temp>echo|set /p="fa28c48dbe49aadebe49aadebe49aadec555a6debf49aaded156a1debf49aade3d55a4deae49aaded156a0de8b49aade3d41f7debb49aadebe49abde9049aadeb86aa0debc49aade52696368be49aade000000000000000000000000000000000000000000000000504500004c01030001ea7f3f0000000000000000e0000f01">>mbenum.hex

...SNIP...

C:\Windows\Temp>   (Progress: 385/388)

C:\Windows\Temp>powershell -Command "$h=Get-Content -readcount 0 -path './mbenum.hex';$l=$h[0].length;$b=New-Object byte[] ($l/2);$x=0;for ($i=0;$i -le $l-1;$i+=2){$b[$x]=[byte]::Parse($h[0].Substring($i,2),[System.Globalization.NumberStyles]::HexNumber);$x+=1};set-content -encoding byte 'mbenum.exe' -value $b;Remove-Item -force mbenum.hex;Get-ChildItem mbenum.exe;"

powershell -Command "$h=Get-Content -readcount 0 -path './mbenum.hex';$l=$h[0].length;$b=New-Object byte[] ($l/2);$x=0;for ($i=0;$i -le $l-1;$i+=2){$b[$x]=[byte]::Parse($h[0].Substring($i,2),[System.Globalization.NumberStyles]::HexNumber);$x+=1};set-content -encoding byte 'mbenum.exe' -value $b;Remove-Item -force mbenum.hex;Get-ChildItem mbenum.exe;"


   (Progress: 386/388)ows\Temp


[i] Done



Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        09/06/2017     10:21      49152 mbenum.exe




C:\Windows\Temp>
```

_NOTE: May need to press enter to get a prompt back at the end._

- - -

## Install

Just exe2hex just requires [Python 3](https://www.python.org/) to function ([Expect](http://expect.sourceforge.net/) is optional for Telnet and WinEXE functions).

Simply add exe2hex a folder in your `$PATH` variable:

```bash
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ curl -k -L "https://raw.githubusercontent.com/g0tmi1k/exe2hex/master/exe2hex.py" > /usr/local/bin/exe2hex
$ chmod 0755 /usr/local/bin/exe2hex
```

### Kali-Linux

exe2hex is already [packaged](https://pkg.kali.org/pkg/exe2hexbat) in [Kali Rolling](https://www.kali.org/), so all you have to-do is:

```bash
root@kali:~# apt install -y exe2hexbat
```


================================================
FILE: exe2hex.py
================================================
#!/usr/bin/env python3

# Name: exe2hex v1.5.2 (2020-04-24) ~ Codename: hEXE
# Author: g0tmilk ~ https://blog.g0tmi1k.com/
# Licence: MIT License ~ http://opensource.org/licenses/MIT
# Credit to: exe2bat.exe & https://github.com/acjsec/exe2bam
# Notes: Could use certutil for base64...

import os
import shutil
import signal
import subprocess
import sys
import tempfile
from optparse import OptionParser

import urllib.parse

version = '1.5.2'


###################
# Functions start #
###################

# Use standard error message and exit
def error_exit(msg):
    error_msg(msg)
    sys.exit(1)


# Standard error message (Red)
def error_msg(msg):
    sys.stderr.write("\033[01;31m[!]\033[00m ERROR: %s\n" % msg)


# Standard success message (Green)
def success_msg(msg):
    print("\033[01;32m[+]\033[00m %s" % msg)


# Verbose message (Yellow)
def verbose_msg(msg):
    if verbose:
        notification_msg(msg)


# Standard notification message (Yellow)
def notification_msg(msg):
    print("\033[01;33m[i]\033[00m %s" % msg)


# Banner information (Blue)
def banner_msg(msg):
    print("\033[01;34m[*]\033[00m %s" % msg)


# CTRL + C
def signal_handler(signal, frame):
    print('Quitting...')
    sys.exit(0)


#################
# Functions End #
#################


###########################
# Start BinaryInput class #
###########################

class BinaryInput:
    # Initialization object configuration
    def __init__(self, exe_file, output_file, method):
        self.exe_file = exe_file  # Full path of the binary input
        self.output_file = output_file # Full path of output file
        self.telnet_file = None  # Full path of the telnet file out
        self.winexe_file = None  # Full path of the winexe file out
        self.exe_filename = ""  # Filename of binary input
        self.output_filename = ""  # Filename of output file
        self.short_file = ""  # Short filename of bat output (8.3 filename)
        self.telnet_filename = ""  # Filename of telnet output
        self.winexe_filename = ""  # Filename of winexe output
        self.exe_bin = b''  # Binary input (data read in)
        self.bin_size = 0  # Binary input (size of data)
        self.byte_count = 0  # How many loops to read in binary
        self.output_hex = ""  # hex format output
        self.method = method # EXE conversion method

        # Extract the input filename from the input path (if there was one)
        if self.exe_file:
            self.exe_file = os.path.abspath(self.exe_file)
            self.exe_filename = os.path.basename(self.exe_file)
        else:
            self.exe_filename = "binary.exe"
        verbose_msg("Output EXE filename: %s" % self.exe_filename)

        # debug.exe has a limitation when renaming files > 8 characters (8.3 filename)
        self.short_file = os.path.splitext(self.exe_filename)[0][:8]
        verbose_msg("Short filename: %s" % self.short_file)

        self.output_filename = os.path.basename(self.output_file)
        verbose_msg("Output filename: %s" % self.output_filename)

    # Make sure the input file exists
    def check_exe(self):
        if not os.path.isfile(self.exe_file):
            error_exit("The input file was not found (%s)" % self.exe_file)

    # Make sure the binary size <= 64k when using bat files (limitation with debug.exe)
    def check_bat_size(self):
        verbose_msg('Binary file size: %s bytes' % self.bin_size)

        if self.bin_size > 65536:
            verbose_msg('Input is larger than 65536 bytes')

    # Try and use strip and/or upx to compress (useful for bat)
    def compress_exe(self):
        notification_msg('Attempting to clone and compress')

        tf = tempfile.NamedTemporaryFile(delete=False)
        notification_msg('Creating temporary file %s' % tf.name)
        try:
            if (self.exe_file):
                shutil.copy2(self.exe_file, tf.name)
            else:
                with open(tf.name, 'wb') as out:
                    out.write(self.exe_bin)
        except:
            error_exit("A problem occurred while trying to clone into a temporary file")

        # Compress the new temp file
        self.compress_exe_strip(tf)

        # Don't do it if its not needed. (AV may detect this)
        if compress == 2:
            self.compress_exe_upx(tf)

        # Set the temp file as the main file
        self.exe_file = os.path.abspath(tf.name)

    # Use strip to compress (useful for bat)
    def compress_exe_strip(self, tf):
        if shutil.which("strip"):
            verbose_msg('Running strip on %s' % tf.name)

            # Get the size before compression
            before_size = os.path.getsize(tf.name)

            # Program to run to compress
            command = "strip -s %s" % tf.name
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
            process.wait()

            # Size after compression
            after_size = os.path.getsize(tf.name)
            diff_size = before_size - after_size

            # Feedback for the user
            success_msg("Compression (strip) was successful! (%s saved)" % ("{:.1%}".format(diff_size / before_size)))
            verbose_msg('Binary file size (after strip) %s' % os.path.getsize(tf.name))
        else:
            error_msg("Cannot find strip. Skipping...")

    # Use UPX to compress (useful for bat). Can be flag'd by AV
    def compress_exe_upx(self, tf):
        if shutil.which("upx"):
            verbose_msg('Running UPX on %s' % tf.name)

            # Get the size before compression
            before_size = os.path.getsize(tf.name)

            # Program to run to compress
            command = "upx -9 -q -f %s" % tf.name
            process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
            process.wait()

            # Size after compression
            after_size = os.path.getsize(tf.name)
            diff_size = before_size - after_size

            # Feedback for the user
            success_msg("Compression (UPX) was successful! (%s saved)" % ("{:.1%}".format(diff_size / before_size)))
            verbose_msg('Binary file size (after UPX) %s' % os.path.getsize(tf.name))
        else:
            error_msg("Cannot find UPX. Skipping...")

    # Get the contents of the input file
    def read_bin_file(self):
        # Feedback for the user, to know where they are
        verbose_msg('Reading binary file')

        # Read the input file
        try:
            with open(self.exe_file, "rb") as f:
                self.exe_bin = f.read()
        except:
            error_exit("A problem occurred while reading the input file (%s)" % self.exe_file)

        # Set the size of the input file
        self.bin_size = os.path.getsize(self.exe_file)

    # Get the contents of STDIN input
    def read_bin_stdin(self):
        # Feedback for the user, to know where they are
        notification_msg('Reading from STDIN')

        # Read from STDIN
        f = ""
        try:
            f = sys.stdin.buffer.read()
        except:
            error_exit('A problem occurred while reading STDIN')

        # Get the length of data read
        stdin_bytes = len(f)

        # Did something go wrong?
        if stdin_bytes == 0:
            error_exit('Zero bytes read from STDIN')

        # Set the size from STDIN
        self.bin_size = stdin_bytes

        # Add the read byte into the byte string
        self.exe_bin = f

    # Convert binary data to a bat file
    def bin_to_bat(self):
        # Feedback for the user, to know where they are
        verbose_msg('Converting to BATch (DEBUG.exe)')

        # Number of 64k+/max_size loops will be the number of parts made/
        x = -1

        # What is tha max size we can use for the loop
        max_size = 65536 - (hex_len * 2)

        # Loop through binary bytes per 65536 (Debug.exe limitation)
        for exeloop in range(0, len(self.exe_bin), max_size):

            # Increase the loop counter (incase the input file is 64k+)
            x += 1

            # Start fresh. Empty the value
            self.byte_count = 0

            # Loop through binary input file for this section
            for i in range(exeloop, exeloop + max_size, hex_len):

                # Is there any more data? Are we at the end?
                if not (self.exe_bin[i:i + hex_len]):
                    break

                # Numbering for the hex position in this loop
                hex_size = (i - (max_size * x)) + (hex_len * 2)

                # Convert to hex and debug.exe format
                self.output_hex += '%secho e %s>>%s.hex%s\r\necho ' % (
                    prefix, '{:04x}'.format(hex_size), self.short_file, suffix)
                self.output_hex += ' '.join('%02x' % y for y in self.exe_bin[i:i + hex_len])
                self.output_hex += '>>%s.hex%s\r\n' % (self.short_file, suffix)

                # Save the amount of data converted - aka byte counter (debug.exe needs it at the end)
                self.byte_count += hex_len

            # Save the bat file
            self.save_bat(x)

            # Start fresh. Empty the value
            self.output_hex = ""

        # Finish off the BATch file (in-case there's multiple parts)
        self.finish_bat(x)

    # Write resulting bat file
    def finish_bat(self, loop=0):
        # Is there more than one part? Going to be using this for the copy fu
        if loop > 0:
            # Loop them all, start with the first
            parts = '%s.0' % self.short_file
            for i in range(1, loop + 1, 1):
                parts += '+%s.%s' % (self.short_file, i)

            # Command fu, to join all the parts together
            output = '%scopy /B /Y %s %s%s\r\n' % (prefix, parts, self.exe_filename, suffix)
        else:
            # Single file, just move it
            output = '%smove /Y %s.%s %s%s\r\n' % (prefix, self.short_file, loop, self.exe_filename, suffix)

        # Select every temp file used, so it can be deleted
        parts = '%s.hex' % self.short_file
        for i in range(0, loop + 1, 1):
            parts += ' %s.%s' % (self.short_file, i)

        # Some times the del command will not remove it (as it is still in use), so let's just null it!
        output += '%secho. >%s.hex%s\r\n' % (prefix, self.short_file, suffix)

        # The final few things
        output += '%sdel /F /Q %s%s\r\n' % (prefix, parts, suffix)
        output += '%sdir %s%s\r\n\r\n' % (prefix, self.exe_filename, suffix)
        #if self.telnet_file == None:
        #    output += '%sstart /wait /b %s%s\r\n\r\n' % (prefix, self.exe_filename, suffix)

        # Write the file out
        self.write_file(self.output_file, output, "BATch (DEBUG.exe)", False)

    # Convert binary data to a PoSh file
    def bin_to_posh(self):
        # Null any previous files
        #self.posh_hex += '%secho|set /p="">%s.hex%s\r\n' % (prefix, self.short_file, suffix)
        self.output_hex += '%secho|set /p="">%s.hex%s\r\n' % (prefix, self.short_file, suffix)

        # Loop through binary bytes
        for i in range(0, len(self.exe_bin), hex_len):
            self.output_hex += '%secho|set /p="' % (prefix)
            self.output_hex += ''.join('%02x' % i for i in self.exe_bin[i:i + hex_len])
            self.output_hex += '">>%s.hex%s\r\n' % (self.short_file, suffix)

    # Write resulting bat file
    def save_bat(self, loop=0):
        # Create bat file!
        output = ""
        output += '%sdebug /?%s\r\n' % (prefix, suffix)
        output += '%sif NOT %%ERRORLEVEL%% == 0 echo &echo &echo &echo **** **** **** **** ****&echo *** Missing DEBUG.exe ***&echo **** **** **** **** ****&exit /b%s\r\n' % (prefix, suffix)
        output += '%secho n %s.%s>%s.hex%s\r\n' % (prefix, self.short_file, loop, self.short_file, suffix)
        output += self.output_hex
        output += '%secho r cx>>%s.hex%s\r\n' % (prefix, self.short_file, suffix)
        output += '%secho %s>>%s.hex%s\r\n' % (prefix, '{:04x}'.format(self.byte_count), self.short_file, suffix)
        output += '%secho w>>%s.hex%s\r\n' % (prefix, self.short_file, suffix)
        output += '%secho q>>%s.hex%s\r\n' % (prefix, self.short_file, suffix)
        output += '%sdebug<%s.hex%s\r\n' % (prefix, self.short_file, suffix)

        # Write file out (Do we need need to overwrite?)
        if loop > 0:
            self.write_file(self.output_file, output, "BATch - DEBUG.exe", False)
        else:
            self.write_file(self.output_file, output, "BATch - DEBUG.exe", True)

    # Write resulting PoSh file
    def save_posh(self):
        # Create PoSh file!
        output = ""
        #output += '%spowershell /?%s\r\n' % (prefix, suffix)
        #output += '%sif NOT %%ERRORLEVEL%% == 0 echo &echo &echo &echo **** **** **** **** ****&echo *** Missing Powershell ***&echo **** **** **** **** ****&exit /b%s\r\n' % (prefix, suffix)
        output += self.output_hex
        output += "%spowershell -Command \"" % (prefix)
        output += "$h=Get-Content -readcount 0 -path './%s.hex';" % (self.short_file)
        output += "$l=$h[0].length;"
        output += "$b=New-Object byte[] ($l/2);"
        output += "$x=0;"
        output += "for ($i=0;$i -le $l-1;$i+=2)"
        output += "{$b[$x]=[byte]::Parse($h[0].Substring($i,2),[System.Globalization.NumberStyles]::HexNumber);"
        output += "$x+=1};"
        output += "set-content -encoding byte '%s' -value $b;" % (self.exe_filename)
        output += "Remove-Item -force %s.hex;" % (self.short_file)
        #output += "Get-ChildItem %s;" % (self.exe_filename)
        output += "\"%s\r\n\r\n" % (suffix)
        #output += '%sstart /wait /b %s%s\r\n\r\n' % (prefix, self.exe_filename, suffix)

        # Write file out
        self.write_file(self.output_file, output, "PoSh", True)
        
    # Write resulting certutil BATch file
    def save_certutil_bat(self):
        # Create bat file!
        output = ""
        output += self.output_hex
        output += "%scertutil -f -decodeHex %s.hex %s >nul%s\r\n" % (prefix, self.short_file, self.exe_filename, suffix)
        output += '%sdel /F /Q %s.hex%s\r\n' % (prefix, self.short_file, suffix)
        output += '%sdir %s%s\r\n\r\n' % (prefix, self.exe_filename, suffix)
        
        # Write file out
        self.write_file(self.output_file, output, "BATch - certutil.exe", True)

    # Write resulting expect file
    def save_expect(self, mode=''):
        port = '0'
        cmd = ""
        file = ""
        infilename = ""
        infile = ""

        if mode == "telnet":
            port = "23"
            #cmd = "telnet -l $username $ip"
            cmd = "telnet $ip"
            file = self.telnet_file
            verbose_msg("Telnet filename: %s" % self.telnet_filename)
        elif mode == "winexe":
            cmd = "winexe -U $username%$password //$ip cmd.exe"
            file = self.winexe_file
            verbose_msg("WinEXE filename: %s" % self.winexe_filename)
        else:
            error_exit("Unexpected mode: %s" % mode)

            infilename = self.output_filename
            infile = self.output_file

        output = ("""#!/usr/bin/expect -f
set timeout 10
set ip [lindex $argv 0]
#set port {0}
set username [lindex $argv 1]
set password [lindex $argv 2]
set file_out {1}
set file_in {2}
set prompt "C:"
## Read in command arguments
if {{ [llength $argv] < 3 }} {{
  set name [file tail $argv0]
  send_user "Usage: ./$name <ip> <username> <password>\\n"; exit 1
}}
## Check to see if the input file is there
if {{ ! [file exist $file_in] }} {{
  send_user "\\n\\n\\[!\\] $file_in is missing\\n"; exit 1
}}
## Connect
send_user "\\n"
spawn {3}
send_user "\\n"
""").format(port, infilename, infile, cmd)

        if mode == "telnet":
            output += ("""## If there is a user name prompt (as -l doesn't always work)
expect "login: " { send "$username\\r" }
## Wait for password prompt
expect {
  "password: " { send "$password\\r" }
  timeout { send_user "\\n\\n\\[!\\] Failed to get password prompt\\n"; exit 1 }
}
""")

        output += ("""## Move to a commonly writeable folder
expect "$prompt" { send "cd %TEMP%\\r" }
## Test write access
set rand [ expr floor( rand() * 999900 ) ]
expect "$prompt" { send "echo $rand>$file_out\\r" }
expect "$prompt" { send "type $file_out\\r" }
expect {
  "$rand" { send_user "\\n\\n\\[i\\] Writeable folder!\\n" }
  timeout { send_user "\\n\\n\\[!\\] Failed to write out\\n"; exit 1 }
}
""")

        if mode == "telnet":
            output += ("""## Restore prompt
expect "$prompt" { send "\\r\\n" }
""")

        output += ("""## Clean up
expect "$prompt" {{ send "del /F $file_out\\r" }}
## Read in our file
set f [open "$file_in"]
set data [read $f]
close $f
## Set counters
set i 0
set total [llength [split $data \\n]]
## For each line, wait for a prompt
foreach line [ split $data \\n ] {{
  ## Skip over empty lines
  if {{ $line eq {{}} }} continue
  ## Increase counter
  incr i
  ## Double carriage return here (don't ask)
  expect "$prompt" {{ send "$line\\r\\r" }}
  ## Fix a telnet issues due to its output (don't ask) - progress isn't required
  expect "$prompt" {{ send_user "   (Progress: $i/$total)\\n" }}
}}
## Show output
#expect "$prompt" {{ send "dir {0}" }}
send_user "\\n\\n\[i\] Done\\n"
## Start
#expect "$prompt" {{ send "start /wait /b {0}" }}
""").format(self.exe_filename)

        if mode == "telnet":
            output += ("""## Restore prompt
expect "$prompt" { send "\\r\\n" }
""")

        output += ("""## Give control back to user afterwards
interact
""")
        # Write file out
        self.write_file(file, output, "Expect", True)

        # Make the file executable
        os.chmod(file, 0o755)

    # Write output
    def write_file(self, filepath, contents, type, overwrite=True):
        # Do we need to HTML encode it?
        if encode:
            contents = urllib.parse.quote_plus(contents).replace("%0D%0A", "\r\n")

        if os.path.isfile(filepath) and overwrite:
            verbose_msg("File already exists. Overwriting %s" % filepath)

        # Try and write the file out to disk
        try:
            if overwrite:
                f = open(filepath, 'w')
            else:
                f = open(filepath, 'a')
            f.write(contents)
            f.close
            if overwrite:
                success_msg("Successfully wrote (%s) %s" % (type, os.path.abspath(filepath)))
        except:
            error_msg("A problem occurred while writing (%s)" % filepath)

    # Main action
    def run(self):
        # Read binary data (file or STDIN?)
        if self.exe_file != None:
            # If there is a EXE input, check its valid
            self.check_exe()
            # If we are to compress, now is the time!
            if compress:
                self.compress_exe()
            self.read_bin_file()
        else:
            self.read_bin_stdin()
            # If we are to compress, now is the time & re-read it in
            if compress:
                self.compress_exe()
                self.read_bin_file()

        # Make BATch file (DEBUG.exe)
        if self.method == "1":
            self.check_bat_size()
            self.bin_to_bat()
        
        # Make BATch file (certutil.exe) 
        if self.method == "2":
            # Feedback for the user, to know where they are
            verbose_msg('Converting to BATch (certutil.exe)')
            self.bin_to_posh()
            self.save_certutil_bat()
        
        # Make PoSh file
        if self.method == "3":
            # Feedback for the user, to know where they are
            verbose_msg('Converting to PoSh')
            self.bin_to_posh()
            self.save_posh()

        # Make Expect/Telnet file
        if telnet:
            # Are we to make a telnet file (bat)?
            if self.output_file != None:
                if self.method == 1:
                    self.telnet_filename = "%s-bat-telnet" % (self.short_file)
                    self.telnet_file = os.path.abspath(self.output_file.replace(self.output_filename, self.telnet_filename))
                    self.save_expect('telnet')
            # Are we to make a telnet file (PoSh)?
            if self.posh_file != None:
                self.telnet_filename = "%s-posh-telnet" % (self.short_file)
                self.telnet_file = os.path.abspath(self.output_file.replace(self.output_filename, self.telnet_filename))
                self.save_expect('telnet')

        # Make Expect/WinEXE file
        if winexe:
            # Are we to make a winexe file (bat)?
            if self.bat_file != None:
                self.winexe_filename = "%s-bat-winexe" % (self.short_file)
                self.winexe_file = os.path.abspath(self.output_file.replace(self.output_filename, self.winexe_filename))
                self.save_expect('winexe')
            # Are we to make a winexe file (PoSh)?
            if self.posh_file != None:
                self.winexe_filename = "%s-posh-winexe" % (self.short_file)
                self.winexe_file = os.path.abspath(self.output_file.replace(self.output_filename, self.winexe_filename))
                self.save_expect('winexe')

#########################
# End BinaryInput class #
#########################


signal.signal(signal.SIGINT, signal_handler)


################
# Main Program #
################

# Only run if we are being used as stand-alone script
if __name__ == "__main__":
    # Display banner
    banner_msg('exe2hex v%s' % version)

    # Configure command-line option parsing
    parser = OptionParser()
    parser.add_option("-x", dest="exe",
                      help="The EXE binary file to convert", metavar="EXE")

    parser.add_option("-s", dest="stdin",
                      help="Read from STDIN", action="store_true", metavar="STDIN")
                      
    parser.add_option("-m", dest="method",
                      help="""METHOD to convert the EXE:\t\t\t
                      1 - DEBUG.exe method (.bat) - x86\t\t
                      2 - certutil.exe method (.bat) - x86/x64 (DEFAULT)\t 
                      3 - PowerShell method (.cmd) - x86/x64""", 
                      default='2',
                      choices=['1','2','3'], 
                      metavar="METHOD")

    parser.add_option("-o", dest="output_file",
                      help="Output file", metavar="output")

    parser.add_option("-e", dest="encode", default=False,
                      help="URL encode the output", action="store_true", metavar="ENCODE")

    parser.add_option("-r", dest="prefix", default='',
                      help="pRefix - text to add before the command on each line", metavar="TEXT")

    parser.add_option("-f", dest="suffix", default='',
                      help="suFfix - text to add after the command on each line", metavar="TEXT")

    parser.add_option("-l", dest="hex_len", default=128,
                      help="Maximum HEX values per line", metavar="INT")

    parser.add_option("-c", dest="compress", default=False,
                      help="Clones and compress the file before converting (-cc for higher compression)",
                      action="count", metavar="COMPRESS")

    parser.add_option("-t", dest="telnet", default=False,
                      help="Create a Expect file, to automate to a Telnet session.",
                      action="store_true", metavar="TELNET")

    parser.add_option("-w", dest="winexe", default=False,
                      help="Create a Expect file, to automate to a WinEXE session.",
                      action="store_true", metavar="WINEXE")

    parser.add_option("-v", dest="verbose", default=False,
                      help="Enable verbose mode", action="store_true", metavar="VERBOSE")

    # Store command-line options and arguments in variables
    (options, args) = parser.parse_args()
    exe = options.exe
    stdin = options.stdin
    method = options.method
    output_file = options.output_file
    encode = options.encode
    prefix = options.prefix
    suffix = options.suffix
    try:
        hex_len = int(options.hex_len)
    except:
        error_exit('Invalid length for -l %s' % options.hex_len)
    compress = options.compress
    telnet = options.telnet
    winexe = options.winexe
    verbose = options.verbose

    # Being helpful if they haven't read -h...
    if len(sys.argv) == 2:
        exe = sys.argv[1]
        print('')
        notification_msg("Next time use \"-x\".   e.g.: %s -x %s" % (sys.argv[0], exe))
        print('')
    # Are there any arguments?
    elif len(sys.argv) <= 1:
        print('')
        print("Encodes an executable binary file into ASCII text format")
        print("Restore using different methods (BATch or PoSh)")
        print('')
        print("Quick Guide:")
        print(" + Input binary file with -s or -x")
        print(" + Output with -o")
        print("Example:")
        print(" $ %s -x /usr/share/windows-binaries/sbd.exe" % sys.argv[0])
        print(" $ %s -x /usr/share/windows-binaries/nc.exe -m 2 -o /var/www/html/nc.txt -cc" % sys.argv[0])
        print(" $ cat /usr/share/windows-binaries/whoami.exe | %s -s -m 3 -o ps.cmd" % sys.argv[0])
        print('')
        print('--- --- --- --- --- --- --- --- --- --- --- --- --- --- ---')
        print('')
        parser.print_help()
        sys.exit(1)

    # Any input methods?
    if exe == None and stdin == None:
        error_exit("Missing a executable file (-x <file>) or STDIN input (-s)")

    # Too many input methods?
    if exe != None and stdin != None:
        error_exit('Cannot use both a file and STDIN for inputs at the same time')
    
    # If output file was not provided, create one based on the selected method
    if output_file == None:
        if method == "1":
            exe_filename = os.path.splitext(os.path.basename(exe))[0]
            output_file = '%s.bat' % os.path.abspath(exe_filename)
            notification_msg("Outputting to %s (BATch - DEBUG.exe)" % (output_file))
        
        if method == "2":
            exe_filename = os.path.splitext(os.path.basename(exe))[0]
            output_file = '%s.bat' % os.path.abspath(exe_filename)
            notification_msg("Outputting to %s (BATch - certutil.exe)" % (output_file))
        
        if method == "3":
            exe_filename = os.path.splitext(os.path.basename(exe))[0]
            output_file = '%s.cmd' % os.path.abspath(exe_filename)
            notification_msg("Outputting to %s (PoSh)" % (output_file))
            
    # Is someone going to overwrite what they put in?
    if stdin != None and (exe == output_file):
        error_exit('Cannot use the same input as output')

    # Read in file information
    x = BinaryInput(exe, output_file, method)

    # GO!
    x.run()
Download .txt
gitextract_yc0xu9k8/

├── LICENSE
├── README.md
└── exe2hex.py
Download .txt
SYMBOL INDEX (25 symbols across 1 files)

FILE: exe2hex.py
  function error_exit (line 27) | def error_exit(msg):
  function error_msg (line 33) | def error_msg(msg):
  function success_msg (line 38) | def success_msg(msg):
  function verbose_msg (line 43) | def verbose_msg(msg):
  function notification_msg (line 49) | def notification_msg(msg):
  function banner_msg (line 54) | def banner_msg(msg):
  function signal_handler (line 59) | def signal_handler(signal, frame):
  class BinaryInput (line 73) | class BinaryInput:
    method __init__ (line 75) | def __init__(self, exe_file, output_file, method):
    method check_exe (line 107) | def check_exe(self):
    method check_bat_size (line 112) | def check_bat_size(self):
    method compress_exe (line 119) | def compress_exe(self):
    method compress_exe_strip (line 144) | def compress_exe_strip(self, tf):
    method compress_exe_upx (line 167) | def compress_exe_upx(self, tf):
    method read_bin_file (line 190) | def read_bin_file(self):
    method read_bin_stdin (line 205) | def read_bin_stdin(self):
    method bin_to_bat (line 230) | def bin_to_bat(self):
    method finish_bat (line 278) | def finish_bat(self, loop=0):
    method bin_to_posh (line 310) | def bin_to_posh(self):
    method save_bat (line 322) | def save_bat(self, loop=0):
    method save_posh (line 342) | def save_posh(self):
    method save_certutil_bat (line 366) | def save_certutil_bat(self):
    method save_expect (line 378) | def save_expect(self, mode=''):
    method write_file (line 494) | def write_file(self, filepath, contents, type, overwrite=True):
    method run (line 516) | def run(self):
Condensed preview — 3 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (43K chars).
[
  {
    "path": "LICENSE",
    "chars": 1075,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 g0tmi1k\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 12768,
    "preview": "# exe2hex\n\nInline file transfer using in-built Windows tools (`DEBUG.exe` or PowerShell).\n\n<p align=\"center\">\n  <img src"
  },
  {
    "path": "exe2hex.py",
    "chars": 26791,
    "preview": "#!/usr/bin/env python3\n\n# Name: exe2hex v1.5.2 (2020-04-24) ~ Codename: hEXE\n# Author: g0tmilk ~ https://blog.g0tmi1k.co"
  }
]

About this extraction

This page contains the full source code of the g0tmi1k/exe2hex GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 3 files (39.7 KB), approximately 11.3k tokens, and a symbol index with 25 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!