6
\$\begingroup\$

I've made a small script to compile Go code with Python.

The result is not something I'm happy with, because:

  • It seems overly complicated
  • I'm worried the nested if-else with f string are unreadable
  • Am I using the subprocess module the correct way?

Any review is welcome.

#!/usr/bin/env python3

import argparse
import subprocess
import os

ARCHS = {
    "32": "386",
    "64": "amd64"
}

BUILDTYPES = ["exe", "dll"]

def parse_arguments():
    parser = argparse.ArgumentParser(usage='%(prog)s [options] <buildtype> <architecture>',
                                     description='Go builder',
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     epilog='''
Python program to build go payloads

Examples:
    ./builder exe 32 --stripped
    ./builder dll 64 --upx --stripped
    ./builder dll 32
''')
    parser.add_argument("buildtype", help="The type to build the paylopad with", type=str)
    parser.add_argument("architecture", help="The architecture to build the paylopad with", type=str, default="32")
    parser.add_argument("--stripped", "-s", help="Strip the payload of symbols", action="store_true", default=False)
    parser.add_argument("--upx", "-u", help="Pack the payload with upx", action="store_true", default=False)
    arguments = parser.parse_args()

    if not arguments.buildtype.lower() in BUILDTYPES:
        parser.error(f"{arguments.buildtype} is not a valid buildtype can only be {', '.join(BUILDTYPES)}")

    if not arguments.architecture.lower() in ARCHS:
        parser.error(f"{arguments.architecture} is not a valid architecture can only be {', '.join(ARCHS.keys())}")
    arguments.architecture = ARCHS[arguments.architecture]

    return arguments

def build_go(**kwargs):
    enviroment = {
        **os.environ,
        "GOOS": "windows",
        "GOARCH": f"{kwargs['architecture']}"
    }
    if kwargs['buildtype'].lower() == "dll":
        enviroment["CGO_ENABLED"] = "1"
        enviroment["CC"] = f"{'i686' if kwargs['architecture'] == '386' else 'x86_64'}-w64-mingw32-gcc"

    build_type = f"go build -buildmode=c-shared" if kwargs["buildtype"].lower() == "dll" else "go build -tags exe"
    stripped = "-s -w" if kwargs['stripped'] else ""
    builder = f'''{build_type} -o GOLoader.{kwargs['buildtype']} -ldflags "-H=windowsgui {stripped}"'''
    subprocess.check_output(builder, shell=True, env=enviroment)

    if kwargs["stripped"]:
        stripped = f"strip GOLoader.{kwargs['buildtype']}"
        subprocess.check_output(stripped, shell=True)

    if kwargs["upx"]:
        upx = f"upx -f GOLoader.{kwargs['buildtype']} -9 --ultra-brute -o GOLoader.upx.{kwargs['buildtype']}"
        subprocess.check_output(upx, shell=True)

if __name__ == '__main__':
    args = parse_arguments()
    build_go(**vars(args))
\$\endgroup\$
1
  • \$\begingroup\$ I would write it using go, so it does not depend upon any runtime dependencies. \$\endgroup\$ Commented Nov 19, 2019 at 9:50

1 Answer 1

5
\$\begingroup\$

Some minor notes.

I love the ternary operator and f strings, but...

Here are three ways to express the same thing. There is the original way, a DRY version of the original way, and a more traditional if/else. Which is easier to read and understand what is going on?

# variant 1
build_type = f"go build -buildmode=c-shared" if kwargs["buildtype"].lower() == "dll" else "go build -tags exe"

# variant 2
build_type = f"go build {'-buildmode=c-shared' if kwargs['buildtype'].lower() == 'dll' else '-tags exe'}"

# variant 3
if kwargs["buildtype"].lower() == "dll":
    build_type = "go build -buildmode=c-shared"
else:
    build_type = "go build -tags exe"

In this case I would argue that the third variant is MUCH easier to understand what the distinction is between the two build types. EG: What question is being asked (buildtype) and what is the difference in the result based on the answer.

Environment has a "n" after the "o"

This:

enviroment["CGO_ENABLED"] = "1"

should likely be:

environment["CGO_ENABLED"] = "1"    

Click is the thing.

Not sure this is immediately relevant as a code review, but...

I much prefer click to argparse. In this simple case there is not likely a huge advantage for Click, but there is little downside. And if you get comfortable with click and the size of your project increases, I think Click can be quite advantageous.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.