#!/usr/bin/env python
#-*-coding:utf-8-*-
#
# $Id: debugtrick.py,v 0.6 2009/03/09 09:54:59 st3f Exp $
#
# This program is made to simplify the process of transferring binary
# files over an ASCII connection to a Microsoft Windows box. It will split
# a binary file into several 65535 byte parts and create an ASCII text
# script for each of them. These scripts, when fed to DEBUG.EXE, will
# output the same binary file parts which can then be concatenated with
# Windows' copy command.
# 
# All the actions that are required at the Windows
# side can be done with tools that ship with Windows by default (at least
# up to and including Windows Vista). NB that .dbg files that are created
# will overwrite any existing files.
#
# Example session:
#
# $ ls -l psexec.exe
# -rw-r--r-- 1 st3f st3f 135168 2009-03-09 10:34 psexec.exe
# $ debugtrick.py psexec.exe pse
# writing pse00.dbg
#   indata: 65535 bytes
#   outdata: 65535 bytes
#
# writing pse01.dbg
#   indata: 65535 bytes
#   outdata: 65535 bytes
#
# writing pse02.dbg
#   indata: 4098 bytes
#   outdata: 4098 bytes
#
# done, wrote 3 scripts
# $ ls -l pse*
# -rw-rw-r-- 1 st3f st3f 229405 2009-03-09 10:35 pse00.dbg
# -rw-rw-r-- 1 st3f st3f 229405 2009-03-09 10:35 pse01.dbg
# -rw-rw-r-- 1 st3f st3f  14382 2009-03-09 10:35 pse02.dbg
# -rw-r--r-- 1 st3f st3f 135168 2009-03-09 10:34 psexec.exe
#
# Then, after transferring the .dbg files over the alfanumeric connection
# to the Windows system, feed them to DEBUG.EXE and then concatenate the
# .bin files that were created (remember the NUL redirection or your shell
# will be flooded):
#
# C:\> debug < pse00.dbg > NUL
# C:\> debug < pse01.dbg > NUL
# C:\> debug < pse02.dbg > NUL
# C:\> copy /b pse00.bin+pse01.bin+pse02.bin psexec.exe
#
# Copyright (c) 2007-2009, st3f, http://www.bigpointyteeth.se/
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

import os, sys
from sys import stderr as err

def usage():
    me = os.path.basename(sys.argv[0])
    print "\ntake a binary file, split it and create DEBUG.EXE scripts"
    print "usage:   %s <binary file> <out file w/o extension>" % me
    print "example: %s psexec.exe psexec" % me
    print "(keep the out file to six characters or less)"

def zerorangem(n, m, digits=1):
    """Return a list of strings from n to m-1 with leading zeroes.
    n=9,m=15             =>  09, 10, 11, ..., 14
    n=3,m=325            =>  003, 004, ..., 324
    n=1,m=3,digits=4     =>  0001, 0002, 0003
    n=993,m=998,digits=2 =>  993, 994, ..., 997"""
    zeroes = "0"
    if len(str(m)) > digits:
        digits = len(str(m))
        zeroes = "0" * digits
    return [(zeroes + str(i))[-digits:] for i in range(n, m)]


# check command line arguments
if len(sys.argv) != 3:
    print>>err,"%s: bad number of arguments" % os.path.basename(sys.argv[0])
    usage()
    sys.exit(1)

# read in the binary file
try:
    binary_file = open(sys.argv[1], "rb")
    binary_data = binary_file.read()
except IOError:
    print>>err,"%s: cannot read '%s'" % (os.path.basename(sys.argv[0]), sys.argv[1])
    sys.exit(1)

binary_file.close()

outname = sys.argv[2]   # base file name for the output files
binary_parts = []

# check if we need to split the binary file
if len(binary_data) <= 0xffff:
    # the file is less than 65535 bytes, no need to split
    binary_parts.append(binary_data)
else:
    # we must split the file into 65535 byte pieces
    j = 0
    while j <= len(binary_data):
        try:
            binary_parts.append(binary_data[j:j+65535])
        except IndexError:
            binary_parts.append(binary_data[j:])
        j += 65535

seq = zerorangem(0, len(binary_parts), digits=2)
j = 0

# generate the debug scripts and save them to files
for part in binary_parts:
    filename = "%s%s" % (outname, seq[j])
    j += 1
    # TODO try/except on outputfiles?
    outfile = open(filename + ".dbg", "wb")
    i = 0
    r = 0x0

    outfile.write("n %s.bin" % filename)

    while i < len(part):
        if i % 0x10 == 0:
            outfile.write("\r\ne %04x" % r)
            r += 0x10
        outfile.write(" " + ("0" + hex(ord(part[i]))[2:])[-2:])
        i += 1

    outfile.write("\r\nrcx\r\n")
    outfile.write(hex(len(part))[2:] + "\r\n")
    outfile.write("w 0\r\nq\r\n")
    outfile.close()

    print "writing %s.dbg" % filename
    print "  indata: %d bytes" % len(part)
    print "  outdata: %d bytes\n" % i

print "done, wrote %d scripts" % j
# EOF
