# tl;dr
I've recently stumbled upon [pybof](https://github.com/rkbennett/pybof) by rkbennet, which is a **.pyd** rebuild of [COFFLoader](https://github.com/trustedsec/COFFLoader/) by @trustedsec. I immediately thought of the _awesome_ [pythonmemorymodule](https://github.com/naksyn/PythonMemoryModule) project by @naksyn, which can also be used to run Beacon Object Files in Python, except in memory, by reflecting a COFFLoader DLL.
This means that it is possible to load BOFs in python by pure reflection, with no additional native dependencies whatsoever. For a concrete example, jump to the [[python-inmemory-bof#code]] or go to https://github.com/zimnyaa/inmembof.py.
# COFFLoader fork
It is much easier to use the Sliver fork of COFFLoader found [here](https://github.com/sliverarmory/COFFLoader/), as it introduces the `LoadAndRun` function that supports getting the BOF output with a callback. The approach is practically the same as in [[bof-lazy-loading]], except in Python this time. The illustration in the article is also relevant here:
```j
load COFFLoader reflectively in-memory ->
GetProcAddress(LoadAndRun) ->
build argument array ->
LoadAndRun(COFF, callback) ->
wait for callback and process output
```
# modifications
A small modification has to be made to `pythonmemorymodule` for it to return the raw address of the `LoadAndRun` function:
```python
def get_proc_addr_raw(self, name_or_ordinal):
codebase = self._codebaseaddr
if not hasattr(self, '_exports_'):
directory = self.OPTIONAL_HEADER.DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_EXPORT]
# No export table found
if directory.Size <= 0: raise WindowsError('No export table found.')
self._exports_ = IMAGE_EXPORT_DIRECTORY.from_address(codebase + directory.VirtualAddress)
if self._exports_.NumberOfFunctions == 0:
# DLL doesn't export anything
raise WindowsError('DLL doesn\'t export anything.')
targ = type(name_or_ordinal)
if targ in [ str, str, str ]:
name_or_ordinal = str(name_or_ordinal)
procaddr_func = self._proc_addr_by_name
elif targ in [ int, int ]:
name_or_ordinal = int(name_or_ordinal)
procaddr_func = self._proc_addr_by_ordinal
else:
raise TypeError('Don\'t know what to do with name/ordinal of type: %s!' % targ)
if not name_or_ordinal in self._foffsets_:
self._foffsets_[name_or_ordinal] = procaddr_func(name_or_ordinal)
return codebase + self._foffsets_[name_or_ordinal]
```
# code
The main logic looks that way:
```python
RUNCOFFCALLBACK = WINFUNCTYPE(None, c_char_p, c_int)
@RUNCOFFCALLBACK
def pycallback(outstr, len_outstr):
print(outstr)
RUNCOFFPROC = WINFUNCTYPE(c_int, c_char_p, c_int, RUNCOFFCALLBACK)
def bofrun(bofpath, args):
with open("COFFLoader/COFFLoader.x64.dll", 'rb') as f:
coffl = f.read() # DYNAMIC BUFFER POG
bof_path = sys.argv[1]
with open(bofpath, 'rb') as f:
boffile = f.read() # DYNAMIC BUFFER POG
dll = pythonmemorymodule.MemoryModule(data=coffl, debug=False)
runcoff = RUNCOFFPROC(dll.get_proc_addr_raw("LoadAndRun"))
bofdata = BeaconPack()
bofdata.addstr("go")
bofdata.addstr(boffile)
bofdata.addstr(args)
bofdata_buf = bofdata.getbuffer()
runcoff(bofdata_buf, len(bofdata_buf), pycallback)
```
It has been integrated into the `beacon_generate.py` interactive shell for ease of use.
# building and usage
Build the COFFLoader and the example BOF:
```
cd COFFLoader
mingw32-make dll
```
Run the BOF:
![[Pasted image 20230127002602.png]]