#!/usr/bin/env python

"""
dbgtool.py - Portable executable to ASCII debug script converter

Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""

from __future__ import print_function  # 兼容 Python 2 和 3 的 print 函数

import os
import sys

from optparse import OptionError
from optparse import OptionParser

def convert(inputFile):
    """将给定的二进制输入文件转换为 ASCII 调试脚本。"""
    fileStat = os.stat(inputFile)  # 获取文件状态
    fileSize = fileStat.st_size  # 获取文件大小

    # 检查文件大小是否超过 65280 字节（可调试的最大限制）
    if fileSize > 65280:
        print("ERROR: the provided input file '%s' is too big for debug.exe" % inputFile)
        sys.exit(1)  # 如果文件太大，退出程序

    # 开始构建调试脚本
    script = "n %s\nr cx\n" % os.path.basename(inputFile.replace(".", "_"))  # 设置脚本名称
    script += "%x\nf 0100 ffff 00\n" % fileSize  # 写入文件大小信息
    scrString = ""  # 用于构建写入命令
    counter = 256  # 地址计数器从 256 开始
    counter2 = 0  # 0 计数器，用于控制输出的字符数

    # 读取输入文件的二进制内容
    fp = open(inputFile, "rb")
    fileContent = fp.read()  # 读取所有内容

    # 遍历文件的每个字节
    for fileChar in fileContent:
        # 在 Python 3 中字符是字节，直接使用；在 Python 2 中需要使用 ord() 函数
        unsignedFileChar = fileChar if sys.version_info >= (3, 0) else ord(fileChar)

        # 如果字符不是空字节（0），则进行处理
        if unsignedFileChar != 0:
            counter2 += 1  # 增加有效字符计数

            if not scrString:
                # 如果 scrString 为空，开始创建新的写入命令
                scrString = "e %0x %02x" % (counter, unsignedFileChar)
            else:
                # 否则，将字符附加到 scrString 中
                scrString += " %02x" % unsignedFileChar
        elif scrString:
            # 如果遇到空字节，且 scrString 中有内容，需要把当前 scrString 添加到脚本
            script += "%s\n" % scrString
            scrString = ""  # 清空 scrString
            counter2 = 0  # 重置有效字符计数

        counter += 1  # 增加地址计数器

        # 每 20 个有效字符输出一次
        if counter2 == 20:
            script += "%s\n" % scrString  # 将当前 scrString 加入脚本
            scrString = ""  # 清空 scrString
            counter2 = 0  # 重置计数

    # 完成脚本，添加结束指令
    script += "w\nq\n"

    return script  # 返回生成的调试脚本

def main(inputFile, outputFile):
    """主功能，处理文件输入输出。"""
    # 检查输入文件是否是常规文件
    if not os.path.isfile(inputFile):
        print("ERROR: the provided input file '%s' is not a regular file" % inputFile)
        sys.exit(1)  # 如果不是，退出程序

    # 调用转换函数
    script = convert(inputFile)

    # 如果提供了输出文件，写入脚本
    if outputFile:
        with open(outputFile, "w") as fpOut:
            sys.stdout = fpOut  # 将标准输出重定向到输出文件
            sys.stdout.write(script)  # 写入脚本内容
            sys.stdout.close()  # 关闭文件
    else:
        print(script)  # 如果没有输出文件，直接打印脚本

if __name__ == "__main__":
    usage = "%s -i <input file> [-o <output file>]" % sys.argv[0]  # 程序用法说明
    parser = OptionParser(usage=usage, version="0.1")  # 创建解析器

    try:
        # 添加命令行选项
        parser.add_option("-i", dest="inputFile", help="Input binary file")  # 输入文件
        parser.add_option("-o", dest="outputFile", help="Output debug.exe text file")  # 输出文件

        (args, _) = parser.parse_args()  # 解析参数

        if not args.inputFile:
            parser.error("Missing the input file, -h for help")  # 必须提供输入文件

    except (OptionError, TypeError) as ex:
        parser.error(ex)  # 捕获错误并报告

    inputFile = args.inputFile  # 输入文件名
    outputFile = args.outputFile  # 输出文件名

    main(inputFile, outputFile)  # 调用主函数