├── .github
├── banner.png
├── help-message.png
└── python-command-example.png
├── .gitignore
├── LICENSE
├── README.md
├── examples
├── shellcode.s
├── syscalls.s
└── syscalls.s.bin
├── lib
└── shellcode_parser.py
└── shellcoding-companion.py
/.github/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ooggle/shellcoding-companion/548f9997eee0e1f8adbc316bcf1e9f99f1269f96/.github/banner.png
--------------------------------------------------------------------------------
/.github/help-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ooggle/shellcoding-companion/548f9997eee0e1f8adbc316bcf1e9f99f1269f96/.github/help-message.png
--------------------------------------------------------------------------------
/.github/python-command-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ooggle/shellcoding-companion/548f9997eee0e1f8adbc316bcf1e9f99f1269f96/.github/python-command-example.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | wheels/
22 | share/python-wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | *.py,cover
49 | .hypothesis/
50 | .pytest_cache/
51 | cover/
52 |
53 | # Translations
54 | *.mo
55 | *.pot
56 |
57 | # Django stuff:
58 | *.log
59 | local_settings.py
60 | db.sqlite3
61 | db.sqlite3-journal
62 |
63 | # Flask stuff:
64 | instance/
65 | .webassets-cache
66 |
67 | # Scrapy stuff:
68 | .scrapy
69 |
70 | # Sphinx documentation
71 | docs/_build/
72 |
73 | # PyBuilder
74 | .pybuilder/
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | # For a library or package, you might want to ignore these files since the code is
86 | # intended to run in multiple environments; otherwise, check them in:
87 | # .python-version
88 |
89 | # pipenv
90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
93 | # install all needed dependencies.
94 | #Pipfile.lock
95 |
96 | # poetry
97 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
98 | # This is especially recommended for binary packages to ensure reproducibility, and is more
99 | # commonly ignored for libraries.
100 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
101 | #poetry.lock
102 |
103 | # pdm
104 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
105 | #pdm.lock
106 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
107 | # in version control.
108 | # https://pdm.fming.dev/#use-with-ide
109 | .pdm.toml
110 |
111 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
112 | __pypackages__/
113 |
114 | # Celery stuff
115 | celerybeat-schedule
116 | celerybeat.pid
117 |
118 | # SageMath parsed files
119 | *.sage.py
120 |
121 | # Environments
122 | .env
123 | .venv
124 | env/
125 | venv/
126 | ENV/
127 | env.bak/
128 | venv.bak/
129 |
130 | # Spyder project settings
131 | .spyderproject
132 | .spyproject
133 |
134 | # Rope project settings
135 | .ropeproject
136 |
137 | # mkdocs documentation
138 | /site
139 |
140 | # mypy
141 | .mypy_cache/
142 | .dmypy.json
143 | dmypy.json
144 |
145 | # Pyre type checker
146 | .pyre/
147 |
148 | # pytype static type analyzer
149 | .pytype/
150 |
151 | # Cython debug symbols
152 | cython_debug/
153 |
154 | # PyCharm
155 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
156 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
157 | # and can be added to the global gitignore or merged into this file. For a more nuclear
158 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
159 | #.idea/
160 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Ooggle
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | A python script to automatically generate shellcode payload from assembly files.
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ## Requirements
13 |
14 | To use this tool, you first need to install `nasm`, `objdump` and `python3`. You can install them with:
15 |
16 | ```sh
17 | sudo apt install nasm python3
18 | ```
19 |
20 | ## Usage
21 |
22 | ```
23 | $ ./shellcoding-companion.py -h
24 |
25 | __ _ _ _ _ _
26 | / _\ |__ ___| | | ___ ___ __| (_)_ __ __ _
27 | \ \| '_ \ / _ \ | |/ __/ _ \ / _` | | '_ \ / _` |
28 | _\ \ | | | __/ | | (_| (_) | (_| | | | | | (_| |
29 | \__/_| |_|\___|_|_|\___\___/ \__,_|_|_| |_|\__, |_v1.0.1
30 | / __\___ _ __ ___ _ __ __ _ |___/(_) ___ _ __
31 | / / / _ \| '_ ` _ \| '_ \ / _` | '_ \| |/ _ \| '_ \
32 | / /__| (_) | | | | | | |_) | (_| | | | | | (_) | | | |
33 | \____/\___/|_| |_| |_| .__/ \__,_|_| |_|_|\___/|_| |_|
34 | By Ooggle |_| https://twitter.com/Ooggle_
35 |
36 |
37 | usage: ./shellcoding-companion.py [-h] [-o OUTPUT] [--show] [-p2] [-p3] [-P] source [source ...]
38 |
39 | positional arguments:
40 | source NASM source(s) file(s). (Example: shellcode.s)
41 |
42 | options:
43 | -h, --help show this help message and exit
44 | -o OUTPUT, --output OUTPUT
45 | Output file for the shellcode. (default: None)
46 | --show Show the output shellcode. (default: False)
47 |
48 | Output languages
49 |
50 | -p2, --python2 Output python2 command to generate the shellcode from command line. (default: False)
51 | -p3, --python3 Output python3 command to generate the shellcode from command line. (default: False)
52 | -P, --perl Output perl command to generate the shellcode from command line. (default: False)
53 | ```
54 |
55 | ## Example usage
56 |
57 | Echo python command to print the generated shellcode of `examples/shellcode.s`:
58 |
59 | ```shell
60 | ./shellcoding-companion.py examples/shellcode.s -p2
61 | ```
62 |
63 | 
64 |
65 | ## Contributing
66 |
67 | Pull requests are welcome. Feel free to open an issue if you want to add other features.
68 |
69 | Thanks to [@Podalirius](https://github.com/p0dalirius) for helping setting up the project!
70 |
--------------------------------------------------------------------------------
/examples/shellcode.s:
--------------------------------------------------------------------------------
1 | [BITS 64]
2 |
3 | section .text
4 |
5 | global _start
6 |
7 | _start:
8 | push rax
9 | mov rbp, rsp
10 |
11 | ;read from stdin
12 | xor rdx, rdx
13 | add dx, 0xffff
14 | mov rsi, rax
15 | xor rdi, rdi
16 | xor rax, rax
17 | syscall
18 |
19 | ret
20 |
--------------------------------------------------------------------------------
/examples/syscalls.s:
--------------------------------------------------------------------------------
1 | [BITS 64]
2 |
3 | section .text
4 |
5 | global _start
6 |
7 | _start:
8 | %assign i 0
9 | %rep 10
10 | mov rax, i
11 | syscall
12 | %assign i i+1
13 | %endrep
14 |
15 | ret
16 |
--------------------------------------------------------------------------------
/examples/syscalls.s.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ooggle/shellcoding-companion/548f9997eee0e1f8adbc316bcf1e9f99f1269f96/examples/syscalls.s.bin
--------------------------------------------------------------------------------
/lib/shellcode_parser.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # File name : shellcode_parser.py
4 | # Author : Ooggle (@Ooggle_)
5 | # Date created : 21 Jun 2022
6 |
7 | import os
8 |
9 |
10 | def asm_parse(pathname, label_start, opcode_end=False, debug=False, show=False):
11 | objdump = os.popen(f'objdump -d {pathname}').read().split('\n')
12 |
13 | # skip to start of shellcode
14 | i = 0
15 | for i in range(len(objdump)):
16 | if label_start in objdump[i]:
17 | break
18 | i += 1
19 |
20 | shellcode = ''
21 | # start parsing
22 | while i < len(objdump):
23 | if objdump[i][10:32].rstrip() != '':
24 | # show shellcode if needed
25 | if show:
26 | print(objdump[i])
27 | shellcode += objdump[i][10:32].rstrip() + ' '
28 | if opcode_end:
29 | if opcode_end in objdump[i]:
30 | break
31 | i += 1
32 | else:
33 | break
34 |
35 | shellcode_len = len(shellcode.split(' ')) - 1
36 | if debug:
37 | print(f'Shellcode length: {(shellcode_len)}')
38 | print(shellcode)
39 | return shellcode, shellcode_len
40 |
41 |
42 | def asm_build(program_name):
43 | ret = os.system(f'nasm -f elf64 {program_name} -o {program_name}.o && ld -s ./{program_name}.o -o ./{program_name}.bin')
44 | os.system(f'rm -rf {program_name}.o')
45 | return ret
46 |
--------------------------------------------------------------------------------
/shellcoding-companion.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # File name : shellcode_parser.py
4 | # Author : Ooggle (@Ooggle_)
5 | # Date created : 21 Jun 2022
6 |
7 | import os
8 | import sys
9 | from lib.shellcode_parser import asm_parse, asm_build
10 | from binascii import unhexlify
11 | import argparse
12 |
13 | version_number = '1.0.1'
14 |
15 | banner = f"""\x1b[0;33m
16 | __ _ _ _ _ _
17 | / _\ |__ ___| | | ___ ___ __| (_)_ __ __ _
18 | \ \| '_ \ / _ \ | |/ __/ _ \ / _` | | '_ \ / _` |
19 | _\ \ | | | __/ | | (_| (_) | (_| | | | | | (_| |
20 | \__/_| |_|\___|_|_|\___\___/ \__,_|_|_| |_|\__, |_\x1b[1;33mv{version_number}\x1b[0;33m
21 | / __\___ _ __ ___ _ __ __ _ |___/(_) ___ _ __
22 | / / / _ \| '_ ` _ \| '_ \ / _` | '_ \| |/ _ \| '_ \
23 | / /__| (_) | | | | | | |_) | (_| | | | | | (_) | | | |
24 | \____/\___/|_| |_| |_| .__/ \__,_|_| |_|_|\___/|_| |_|
25 | \x1b[0;1;3mBy Ooggle\x1b[0;33m |_| \x1b[0;1mhttps://twitter.com/Ooggle_\x1b[0m
26 |
27 | """
28 |
29 | class CustomParser(argparse.ArgumentParser):
30 | def error(self, message):
31 | sys.stderr.write('Error: %s\n' % message)
32 | self.print_help()
33 | sys.exit(2)
34 |
35 | def parse_args():
36 | parser = argparse.ArgumentParser(
37 | prog=sys.argv[0],
38 | formatter_class=argparse.ArgumentDefaultsHelpFormatter
39 | )
40 | parser.add_argument("-o", "--output", default=None, help="Output file for the shellcode.")
41 | parser.add_argument("--show", default=False, action="store_true", help="Show the output shellcode.")
42 |
43 | group = parser.add_argument_group(description="Output languages")
44 | group.add_argument("-p2", "--python2", default=False, action="store_true", help='Output python2 command to generate the shellcode from command line.')
45 | group.add_argument("-p3", "--python3", default=False, action="store_true", help='Output python3 command to generate the shellcode from command line.')
46 | group.add_argument("-P", "--perl", default=False, action="store_true", help='Output perl command to generate the shellcode from command line.')
47 |
48 | parser.add_argument("source", nargs="+", help="NASM source(s) file(s). (Example: shellcode.s)")
49 | args = parser.parse_args()
50 |
51 | return args
52 |
53 |
54 | if __name__ == '__main__':
55 | print(banner)
56 | args = parse_args()
57 |
58 |
59 | if len(os.popen("which nasm").read()) == 0:
60 | print("\x1b[1;31m[!] Missing requirement: nasm\x1b[0m")
61 | exit(0)
62 |
63 | if len(os.popen("which objdump").read()) == 0:
64 | print("\x1b[1;31m[!] Missing requirement: objdump\x1b[0m")
65 | exit(0)
66 |
67 | # building shellcode(s)
68 | print(f'\x1b[1;32m[+] Building shellcode(s): {args.source}\x1b[0m')
69 | for source in args.source:
70 | print(f'\x1b[0;32m[+] {source} ...\x1b[0m', end='')
71 | if asm_build(source) != 0:
72 | print(f'\x1b[1;31m[!] ERROR: build failed for "{source}"\x1b[0m')
73 | exit(1)
74 | print(f'\x1b[1;32m Success!\x1b[0m')
75 |
76 | # parsing shellcode(s)
77 | print('\n\x1b[1;32m[+] Parsing shellcode(s)\x1b[0m')
78 | bytecodes = []
79 | for source in args.source:
80 | print(f'\x1b[0;32m[+] {source}.bin ...\x1b[0m')
81 | (tmpbytes, _) = asm_parse(f'{source}.bin', '<.text>')
82 | bytecodes.append([unhexlify(b) for b in tmpbytes.split(' ') if b != ''])
83 |
84 | # saving final shellcode to file
85 | if args.output:
86 | print(f'\n\x1b[1;32m[+] Saving shellcode to "{args.output}"\x1b[0m')
87 |
88 | with open(args.output, 'wb') as f:
89 | for bytecode in bytecodes:
90 | for b in bytecode:
91 | f.write(b)
92 |
93 | print(f'\x1b[0;32m[+] Done! {sum([len(b) for b in bytecodes])} bytes written to {args.output}\x1b[0m')
94 |
95 | if args.python2:
96 | print(f'\n\x1b[1;32m[+] Generating python command\x1b[0m')
97 |
98 | hex_bytecodes = ""
99 | for bytecode in bytecodes:
100 | hex_bytecode = ''.join([
101 | '\\x' + hex(int.from_bytes(c, 'big'))[2:].rjust(2, '0')
102 | for c in bytecode
103 | ])
104 | hex_bytecodes += hex_bytecode
105 |
106 | print(f'python2 -c \'print "{hex_bytecodes}"\'')
107 |
108 | if args.python3:
109 | print(f'\n\x1b[1;32m[+] Generating python command\x1b[0m')
110 |
111 | hex_bytecodes = ""
112 | for bytecode in bytecodes:
113 | hex_bytecode = ''.join([
114 | '\\x' + hex(int.from_bytes(c, 'big'))[2:].rjust(2, '0')
115 | for c in bytecode
116 | ])
117 | hex_bytecodes += hex_bytecode
118 |
119 | print(f'python3 -c \'print "{hex_bytecodes}"\'')
120 |
121 | if args.perl:
122 | print(f'\n\x1b[1;32m[+] Generating perl command\x1b[0m')
123 |
124 | hex_bytecodes = ""
125 | for bytecode in bytecodes:
126 | hex_bytecode = ''.join([
127 | '\\x' + hex(int.from_bytes(c, 'big'))[2:].rjust(2, '0')
128 | for c in bytecode
129 | ])
130 | hex_bytecodes += hex_bytecode
131 |
132 | print(f'perl -e \'print "{hex_bytecodes}"\'')
133 |
134 | if args.show:
135 | print(f'\n\x1b[1;32m[+] Showing output shellcode\x1b[0m')
136 |
137 | for source in args.source:
138 | print(f'\x1b[0;32m[+] {source}.bin ...\x1b[0m')
139 | (tmpbytes, _) = asm_parse(f'{source}.bin', '<.text>', show=True)
140 |
141 | print('\n\x1b[1;32m[+] Done!\x1b[0m\n')
142 |
--------------------------------------------------------------------------------