# 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]]