Skip to content

Instantly share code, notes, and snippets.

@Still34
Last active June 16, 2023 07:04
Show Gist options
  • Save Still34/d26a1c0164861aabfa2ad4ce75de520d to your computer and use it in GitHub Desktop.
Save Still34/d26a1c0164861aabfa2ad4ce75de520d to your computer and use it in GitHub Desktop.
Dumps the inner scripts generated by Kodak Faith's PS1/BAT to EXE tool
# Made with <3 by Still/Azaka
# https://links.azaka.fun
import hashlib
import logging
import os
import pathlib
import re
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
import lief
logger = logging.getLogger(__file__)
def begin_unpack(filepath: str) -> (str, str):
pe = lief.parse(filepath)
data_section = next(s for s in pe.sections if '.data' == s.name)
secret_offset = data_section.search(b'\x7F\x3B\xD5\x06')
if secret_offset > 0xffffffff:
raise ValueError("Unable to find the secret constant. This payload likely is not using PS1ToExe.")
secret_bytes = bytes(data_section.content[secret_offset:secret_offset + 8])
logger.debug(f"Found secret {secret_bytes.hex()} @ offset {hex(secret_offset)}.")
secret = hashlib.md5(secret_bytes).hexdigest().upper()
logger.info(f"Generated secret {secret} based on bytes @ {hex(secret_offset)}.")
cipher = Cipher(algorithm=algorithms.ARC4(secret.encode()), mode=None)
resource_root = pe.resources
rcdata_node = next(r for r in resource_root.childs if r.id == int(lief.PE.RESOURCE_TYPES.RCDATA))
buffer = []
for entry in rcdata_node.childs:
decryptor = cipher.decryptor()
resource_content = bytes(entry.childs[0].content)
logger.debug(f"Decrypting content {resource_content.hex()} @ resource \"{entry.name}\".")
decrypted_content = decryptor.update(resource_content)
try:
decoded_content = decrypted_content.decode()[:-1]
if len(decoded_content) > 0:
buffer.append(decoded_content)
logger.info(f"Decrypted content at resource \"{entry.name}\".")
except UnicodeError:
logger.warning(f"Failed to decode content \"{decrypted_content.hex()}\" (hexified).")
continue
filename = 'output.ps1'
content = ''
for b in buffer:
if re.findall("^.*.(cmd|bat|ps1)$", b, flags=re.I):
filename = b
else:
content += b
return filename+'-', content
def main():
import coloredlogs
import argparse
parent_argparser = argparse.ArgumentParser()
parent_argparser.add_argument("--verbose", action="store_true", help="Enable verbose logging.")
parent_argparser.add_argument("--simulate", action="store_true", help="Enable simulation mode (no file output).")
parent_argparser.add_argument("input", type=str, help="Path to the file packed using PS1ToExe.")
args = parent_argparser.parse_args()
os.environ['COLOREDLOGS_LOG_FORMAT'] = '[%(asctime)s] %(levelname)s - %(message)s'
coloredlogs.install(level='DEBUG' if args.verbose else 'INFO')
if pathlib.Path(args.input).exists():
try:
(filename, content) = begin_unpack(filepath=args.input)
if not args.simulate:
with open(filename, 'w', encoding="utf-8") as file:
logger.info(f"Writing decoded content to {filename}...")
file.write(content)
except Exception as e:
logger.critical(e)
else:
raise FileNotFoundError("File not found.")
logger.info("Finished.")
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment