pytorch加载pt模型_进行中的恶意软件开发pt 1动态模块加载
pytorch加载pt模型
介绍 (Introduction)
As a blend between offensive security engineer and developer, I find myself frustrated in attempting to adhere to the software development lifecycle (SDLC). The modern day security consultant requires so many disparate tools across a variety of maintainers to be successful in operations, and integrating them into a workflow is awkward at best. Worse, the methods in which these tools are deployed into an environment are often immutable to the user, leaving them with little to no alternatives. Many agents are compiled with a set of static commands, with usually some set of functionality that allows the end user to extend it in a limited capacity. Besides these commands being immutable once compiled and loaded, if an agent were to be compromised by a clever analyst, they’d be able to create detections around it and its entire feature set.
作为进攻性安全工程师和开发人员的混合体,我发现自己对遵守软件开发生命周期(SDLC)感到沮丧。 当今的安全顾问需要跨多个维护人员的众多不同工具才能成功运行,并且将它们集成到工作流中充其量是很尴尬的。 更糟糕的是,将这些工具部署到环境中的方法通常对用户是不可变的,从而几乎没有替代品。 许多代理程序是用一组静态命令编译的,通常具有一些功能,这些功能允许最终用户以有限的能力扩展它。 除了这些命令一旦编译和加载后是不变的,如果一个代理要被一个聪明的分析家破坏,他们将能够围绕它及其整个功能集创建检测。
Instead, what if we could load agent functionality as it was needed? A minimal agent core is delivered to the target whose primary function is to load commands from the control server. Once loaded, the commands could then be executed by the agent, dispatching requisite data to the modules and communicating the results. This is exciting because it solves many of the problems outlined above:
相反,如果我们可以按需加载代理功能怎么办? 最小的代理程序核心将传递给目标,目标核心的主要功能是从控制服务器加载命令。 加载后,命令便可以由代理执行,将必需的数据分发到模块并传达结果。 这很令人兴奋,因为它解决了上面概述的许多问题:
- If any agent in a mesh were compromised, the capabilities exposed would only be limited to that which was loaded in memory at that time. If the agent isn’t carved from memory, a defender only sees the bare-bones loading functionality. If one were to clean up their memory after executing a module, the module functionality also remains safe unless a defender dumps the memory of the machine while that module is executing. 如果网格中的任何代理程序遭到破坏,则公开的功能将仅限于当时在内存中加载的功能。 如果代理不是从内存中分离出来的,则防御者只会看到准系统加载功能。 如果执行模块后要清理其内存,则除非防御者在执行该模块时转储了机器的内存,否则该模块的功能也将保持安全。
- Modules can live as their own separate code repositories, allowing for easier maintainability and QA testing. 模块可以作为自己的独立代码存储库使用,从而使维护和质量检查变得更加容易。
- Modules can be written in any language so long as they compile to shared libraries. 只要模块可以编译到共享库中,就可以用任何语言编写。
-
Modules can have versioning associated with them, which is a model Cody Thomas’s (https://twitter.com/its_a_feature_) Mythic C2 (https://github.com/its-a-feature/Mythic/) framework supports.
模块可以具有与之关联的版本控制,这是Cody Thomas( https://twitter.com/its_a_feature_ )Mythic C2( https://github.com/its-a-feature/Mythic/ )框架支持的模型。
- In shops where a dedicated development team does not exist, and new innovations are driven by individuals, it becomes much easier to integrate new functionality any one person develops either on assessment or otherwise. 在不存在专门的开发团队且个人推动新创新的商店中,集成任何人通过评估或其他方式开发的新功能变得容易得多。
In this article, I’ll outline a proof-of-concept (POC) written in Go that is capable of loading shared libraries during its run time, and demonstrate how to accomplish two-way communication between an arbitrary shared library and the application core. This POC is written for Linux for the sake of simplicity; however, the same concept can be applied to Windows using Stephen Fewer’s excellent Reflective DLL Injection project (https://github.com/stephenfewer/reflectivedllinjection).
在本文中,我将概述用Go编写的概念证明(POC),它可以在运行时加载共享库,并演示如何完成任意共享库和应用程序核心之间的双向通信。 。 为了简单起见,此POC是为Linux编写的。 但是,可以使用Stephen Fewer出色的Reflective DLL注入项目( https://github.com/stephenfewer/reflectivedllinjection )将相同的概念应用于Windows。
为什么去? (Why Go?)
Go was appealing as an application core for three reasons. First, Go is capable of cross-platform compilation. It’d provide a stable code warehouse for every operating system (OS), and building is (for the most part) straight forward for whatever OS you’d want to deploy on. Second, the ability for Go to interact with C code allows a developer to more easily manage C code without having to code in pure C. Moreover, C gives us direct access to native APIs so that we don’t have to perform reflection to access them. Lastly, I wanted to learn a new language and enjoy Go quite a bit. It’s a minor thing, but hey, it’s important to enjoy what you do.
Go之所以吸引其作为应用程序核心,原因有三点。 首先,Go能够进行跨平台编译。 它会为每个操作系统(OS)提供一个稳定的代码仓库,并且(对于大多数情况而言)对于要在其上部署的任何OS而言,构建都是很简单的。 其次,Go与C代码进行交互的能力使开发人员可以更轻松地管理C代码,而不必使用纯C编写代码。此外,C使我们可以直接访问本机API,因此我们不必执行反射即可访问他们。 最后,我想学习一种新的语言,并且相当喜欢Go。 这是一件小事,但是,享受您的工作很重要。
应用程序设计注意事项 (Application Design Considerations)
Before we write any code, we should define what functionality it is we’re trying to build. Our aim is to create a shared library loader that can load libraries in memory, invoke function exports from said library, and return the results of that function. The results will be wildly different from function to function, so results should be stuffed into a datagram structure that both the application and library agree upon. These datagrams should be flexible enough such that when received, the application or library can perform more complex logic with the data within. A module loaded this way may be long running and need to stream output back to the application core. As such, the loading application needs to expose callback functionality a library can call. Conversely, a loaded library may require more data from the application core (such as in the case of chunked file downloads), and thus the library must also have a callback function the application core can feed data into. Both the application and library callback functions must exist for two way communication to occur and is critical for more complex functionality.
在编写任何代码之前,我们应该定义我们要构建的功能。 我们的目标是创建一个共享库加载器,该加载器可以将库加载到内存中,从该库调用函数导出,并返回该函数的结果。 结果因函数而异,因此结果应填充到应用程序和库都同意的数据报结构中。 这些数据报应足够灵活,以使应用程序或库在接收到数据报时可以对其中的数据执行更复杂的逻辑。 以这种方式加载的模块可能会长时间运行,并且需要将输出流回到应用程序核心。 这样,正在加载的应用程序需要公开库可以调用的回调功能。 相反,已加载的库可能需要来自应用程序核心的更多数据(例如在分块下载文件的情况下),因此该库还必须具有应用程序核心可以将数据馈入的回调函数。 应用程序和库回调函数必须同时存在,才能进行双向通信,这对于更复杂的功能至关重要。
These requirements are defined more succinctly as follows:
这些要求的定义如下:
- The application must be capable of loading libraries in memory* (Linux is a special snowflake). 该应用程序必须能够在内存中加载库*(Linux是一种特殊的雪花)。
- The application must be capable of parsing function exports from these in-memory libraries. 该应用程序必须能够解析这些内存库中的函数导出。
- The application must expose an interface for the library so that the newly loaded library can stream data to the application core. 应用程序必须公开库的接口,以便新加载的库可以将数据流传输到应用程序核心。
- The loaded library must expose an interface for the application core to invoke so that it may receive more data (if required). 加载的库必须公开一个接口,供应用程序核心调用,以便它可以接收更多数据(如果需要)。
- Communications must adhere to a datagram specification such that both the application and library can parse received data correctly. 通信必须遵守数据报规范,以便应用程序和库都可以正确解析收到的数据。
CGo简介 (A Brief Foreword on CGo)
Before proceeding into the implementation, I wanted to say a few words on how CGo (e.g., how Go can call C and vice-versa) works. This is by no means a complete primer or replacement for the stellar package documentation (found here: https://golang.org/cmd/cgo/). Instead, I intend to give a high level overview of the critical concepts required to implement this in Go.
在继续实施之前,我想谈谈CGo的工作方式(例如,Go如何调用C,反之亦然)。 这绝不是完整的入门资料或替代恒星软件包文档(可在此处找到: https : //golang.org/cmd/cgo/ )。 相反,我打算对在Go中实现此功能所需的关键概念进行高层概述。
First, C can call Go functions so long as those functions are exported in your package. This is done by the //export flag preceding your Go function definition. You then declare in your C code that there exists some function declared outside the scope of your C file that you can invoke using the extern flag. The documentation is more succinct (see: https://golang.org/cmd/cgo/#hdr-C_references_to_Go), but can be summarized by this excerpt here:
首先,C可以调用Go函数,只要这些函数在包中导出即可。 这是通过Go函数定义之前的// export标志完成的。 然后,您在C代码中声明存在一些在C文件范围之外声明的函数,可以使用extern标志调用该函数。 该文档更加简洁(请参阅: https : //golang.org/cmd/cgo/#hdr-C_references_to_Go ),但是可以通过以下摘录进行总结:
Conversely, Go can invoke C code directly by using the “C” package so long as the C function is defined in the header file and included in the calling Go file. Again, the documentation can demonstrate this clearly here:
相反,只要C函数在头文件中定义并包含在调用的Go文件中,Go便可以使用“ C”包直接调用C代码。 同样,文档可以在这里清楚地证明这一点:
The last key concept critical to this POC is understanding how pointers in applications work. A pointer is an address in memory that points to something, be it an object like a datagram or the address of a function. In this POC we’ll be passing pointers of both datagrams and functions betwixt the libraries and application; however, we cannot pass Go function pointers directly. Moreover, the documentation states that “Calling C function pointers is currently not supported, however you can declare Go variables which hold C function pointers and pass them back and forth between Go and C. C code may call function pointers received from Go.”
此POC的最后一个关键概念是了解应用程序中的指针如何工作。 指针是内存中指向某物的地址,它可以是数据报之类的对象,也可以是函数的地址。 在这个POC中,我们将在库和应用程序之间传递数据报和函数的指针。 但是,我们不能直接传递Go函数指针。 此外,文档指出“当前不支持调用C函数指针,但是您可以声明Go变量,该变量包含C函数指针,并将它们在Go和C之间来回传递。C代码可能调用从Go接收到的函数指针。”
This gives us everything we need for two way communications. Our Go code can expose a function that C can obtain the address to. C can define a function to invoke an arbitrary function pointer, and Go can invoke this newly defined C function. The address of this C function can be passed between the application core and a newly loaded library, which would complete our requirement of two-way communication.
这为我们提供了双向通信所需的一切。 我们的Go代码可以公开C可以获取地址的功能。 C可以定义一个函数来调用任意函数指针,而Go可以调用这个新定义的C函数。 可以在应用程序内核和新加载的库之间传递此C函数的地址,这将满足我们双向通信的要求。
数据报规范 (Datagram Specification)
For the purpose of this article, I’ll define a datagram as a special message type that holds data to be passed between the application core and a library. These datagrams are structs with a predictable memory layout so that (in theory) it’s possible to pass pointers to these structs amongst a variety of languages and receive the datagram properly. In this first iteration, a datagram holds the following data:
出于本文的目的,我将数据报定义为一种特殊的消息类型,其中包含要在应用程序核心和库之间传递的数据。 这些数据报是具有可预测的内存布局的结构,因此(理论上)可以在多种语言之间传递指向这些结构的指针,并正确接收数据报。 在第一次迭代中,数据报保存以下数据:
- The data that is being sent between the application and library. 在应用程序和库之间发送的数据。
- The length of the data being sent. 发送数据的长度。
- The name of the module that is sending or receiving the data. This is important so that the application core can route data from the C2, like a file chunk, to the module requiring the data. 正在发送或接收数据的模块的名称。 这一点很重要,因此应用程序核心可以将数据从C2(例如文件块)路由到需要数据的模块。
- The type of message being sent. This allows for more complex application logic for whatever function is receiving the datagram. 发送消息的类型。 对于接收数据报的任何功能,这都允许使用更复杂的应用程序逻辑。
This is simply the first iteration of the datagram and I’m sure there’s oversights, but for the sake of this proof of concept, it’ll suffice. In the POC code, it’s defined as the following:
这只是数据报的第一次迭代,我敢肯定有疏忽之处,但出于概念验证的目的,就足够了。 在POC代码中,其定义如下:
转到模块规格 (Go Module Specification)
We’re going to start with the module specification as I believe it to be easier to understand than the application core. As discussed earlier, each module needs to adhere to the datagram specification and be able to invoke an application callback function to communicate data to the loader. In our discussion of CGo and function pointers we discussed how Go code cannot invoke C function pointers directly; however, they can define a bridge function in C to invoke these C function pointers. If we can pass the application callback function pointer to the invoked module, that module will be able to invoke that function pointer through the C bridge function.
我们将从模块规范开始,因为我认为它比应用程序核心更容易理解。 如前所述,每个模块都需要遵守数据报规范,并能够调用应用程序回调函数以将数据传递给加载程序。 在我们对CGo和函数指针的讨论中,我们讨论了Go代码如何不能直接调用C函数指针。 但是,他们可以在C中定义一个桥函数来调用这些C函数指针。 如果我们可以将应用程序回调函数指针传递给被调用的模块,则该模块将能够通过C桥函数来调用该函数指针。
A minimal project folder is going to hold three files:
一个最小的项目文件夹将容纳三个文件:
- A file, bridge.h, which declares what a callback function is and the bridge function to invoke that callback function pointer. 一个文件bridge.h,它声明什么是回调函数以及用于调用该回调函数指针的桥函数。
- A file, bridge.c, that defines the bridge function and invokes the function pointer passed to it with some data. 一个文件bridge.c,它定义桥函数并调用传递给它的函数指针和一些数据。
- A file, main.go, that exports two functions — a main and callback function for the application core to invoke. The main executing function of this module will take a pointer to a datagram, a pointer to the application callback function, and a pointer to a datagram struct to be populated by the module. 一个文件main.go,它导出两个函数-一个main和callback函数,供应用程序核心调用。 该模块的主要执行函数将使用一个指向数据报的指针,一个指向应用程序回调函数的指针以及一个将由该模块填充的数据报结构的指针。
Finally, let’s define our Go shared library. For this example, we’re going to export a function named helloworld that’ll be invoked by the loading core. In main.go of our library, we’ll define the function as follows:
最后,让我们定义Go共享库。 在此示例中,我们将导出一个名为helloworld的函数,该函数将由加载核心调用。 在我们库的main.go中,我们将如下定义函数:
As we can see, the function takes three arguments. The first argument is a pointer to a datagram that’s forwarded from the application core. The second argument is a C function pointer to the C bridge function in the application core, such that the invoked module can call the application callback function. Lastly, the third argument is a pointer to the resultant datagram to be populated by the module. In our example, we see on line 6 we type cast the pointer to a datagram structure pointer and print out the data that was passed to the function. Further down, at line 16, we pass data from our module back to the loading application by invoking the bridge function defined in bridge.h. Then, before finishing the routine, we populate the resultant datagram pointer with some data to be parsed by the application core.
如我们所见,该函数接受三个参数。 第一个参数是指向从应用程序核心转发的数据报的指针。 第二个参数是指向应用程序核心中C桥函数的C函数指针,以便被调用的模块可以调用应用程序回调函数。 最后,第三个参数是指向要由模块填充的结果数据报的指针。 在我们的示例中,我们在第6行看到将类型转换为数据报结构指针的指针键入,并打印出传递给函数的数据。 再往下看,在第16行,我们通过调用bridge.h中定义的bridge函数,将数据从模块传递回加载应用程序。 然后,在完成例程之前,我们用一些要由应用程序核心解析的数据填充结果数据报指针。
Our module definition is still incomplete. According to our requirements above, the module may need to receive data from the loading agent, and as such should expose a callback function. We defined our module callback function in our example as follows:
我们的模块定义仍然不完整。 根据我们上面的要求,该模块可能需要从加载代理接收数据,因此应公开一个回调函数。 我们在示例中定义了模块回调函数,如下所示:
This function simply takes a pointer to a datagram, then should perform additional logic based on the message type or type of data it’s expecting to receive from the loading application. You can imagine a global shared data structure in this module that the main helloworld function blocks on until helloworldCallback populates it, then proceeds with whatever application logic it wants to do next.
此函数仅使用指向数据报的指针,然后应根据消息类型或期望从加载应用程序接收的数据类型执行其他逻辑。 您可以想象此模块中的全局共享数据结构,主要的helloworld函数将阻塞在该结构上,直到helloworldCallback填充该结构为止,然后继续下一步要执行的任何应用程序逻辑。
Once this is built into a shared library, we should see two functions exported (helloworld and helloworldCallback) for our loading application to get handles to.
一旦将其构建到共享库中,我们应该看到导出的两个函数(helloworld和helloworldCallback)供加载应用程序获取句柄。
应用核心POC (Application Core POC)
内存中模块加载 (In-Memory Module Loading)
On Linux, this has been a solved problem for quite some time, and this POC doesn’t implement a novel loading solution. It allocates a file in RAM using the memfd suite of functions and bootstraps the library and function calls using dlopen and dlsym respectively. I extended the code from @TheXC3LL’s blog post on the subject (https://x-c3ll.github.io/posts/fileless-memfd_create/) to suit the project requirements by:
在Linux上,这已经解决了很长时间了,并且此POC并未实现新颖的加载解决方案。 它使用memfd函数套件在RAM中分配文件,并分别使用dlopen和dlsym引导库和函数调用。 我通过以下方式扩展了@ TheXC3LL的博客文章中有关该主题的代码( https://x-c3ll.github.io/posts/fileless-memfd_create/ ),以适合项目要求:
- Returning exported function pointers using the dlsym function call. 使用dlsym函数调用返回导出的函数指针。
- Creating data structures to hold requisite function pointers for the main module logic and module callback functions. 创建数据结构来保存主模块逻辑和模块回调函数的必需函数指针。
- Creating Go package wrappers to manage RAM files and in-memory libraries loaded this way (memfile and memlibrary packages respectively). 创建Go包包装器以管理以这种方式加载的RAM文件和内存库(分别为memfile和memlibrary包)。
内存包 (The memlibrary Package)
The memfile package is relatively straight forward, short, and nothing too complex. Rather, I’ll focus on the implementation of the library loading functionality due to the complexity.
memfile软件包相对简单,简短,没有什么复杂的东西。 相反,由于复杂性,我将重点介绍库加载功能的实现。
Let’s first cover what aspects of an in-memory library we’d care about as it pertains to our project goals. First, we want the short name of the library we’re invoking so that we can route data to that library at a later time if it was required. Second, we’d want to know where in memory this library lives, and in our case this will be a pointer to the RAM file created using memfd_create. Lastly, we want to hold function pointers to the exported functions of that library that we care about. Because we defined the specification for a new module above, we only care about two functions: the function that performs the main module logic, and the callback function of that library if it requires more data. Given this, I define an in memory module as follows:
首先,让我们介绍一下与我们的项目目标相关的内存库的哪些方面。 首先,我们要调用的库的简称,以便在需要时可以将数据路由到该库。 其次,我们想知道该库在内存中的位置,在我们的情况下,这将是指向使用memfd_create创建的RAM文件的指针。 最后,我们要保留指向我们关心的该库的导出函数的函数指针。 因为我们在上面为新模块定义了规范,所以我们只关心两个函数:执行主模块逻辑的函数以及该库的回调函数(如果需要更多数据)。 鉴于此,我定义了一个内存中模块,如下所示:
Let’s for a moment take for granted that these function pointers are populated successfully by whatever library loading logic is implemented. We’d need two primary wrappers around each module — one to invoke the main function of the module, and another to send data to that module. In Go, we define this to be as follows:
让我们暂时考虑一下,无论使用哪种库加载逻辑,都可以成功填充这些函数指针。 我们在每个模块周围需要两个主要包装器-一个用于调用模块的主要功能,另一个用于将数据发送到该模块。 在Go中,我们将其定义如下:
We’ll cover how results are passed between the library and the main application on line 11 later on, but for a moment let’s discuss line 5. As shown previously, C code can invoke function pointers to it while CGo cannot. This function, call_module_callback, is a bridge function that allows us to call the module’s exported callback function from Go. The definition for this function is no different than the callback defined in the module’s bridge.c file, so we won’t cover it further here.
稍后我们将在第11行介绍如何在库和主应用程序之间传递结果,但让我们暂时讨论第5行。如前所示,C代码可以调用指向它的函数指针,而CGo不能。 这个函数call_module_callback是一个桥接函数,允许我们从Go调用模块的导出回调函数。 该函数的定义与模块的bridge.c文件中定义的回调没有什么不同,因此我们在这里不再赘述。
The call_module_function is not dissimilar to the call_module_callback function. The only difference between them is the definition of the function pointers they’re invoking — namely, callbacks only take one argument, while a module function requires three. To get the resultant datagram from the module, we define call_module_function as follows:
call_module_function与call_module_callback函数没有什么不同。 它们之间的唯一区别是它们正在调用的函数指针的定义-即,回调仅接受一个参数,而模块函数则需要三个参数。 为了从模块获取结果数据报,我们按如下方式定义call_module_function:
(Note: datagram is the struct defined previously, except redefined in the C header file)
(注意:数据报是先前定义的结构,但在C头文件中已重新定义)
Finally, let’s stitch all the pieces together by creating a new InMemoryModule. Here’s the Go code for declaring a new module:
最后,让我们通过创建一个新的InMemoryModule将所有部分拼接在一起。 这是用于声明新模块的Go代码:
Let’s first start by inspecting the parameters. We need to know where in memory this library is located, hence the requirement for the InMemoryFile. Next, if we want to route data from the application to the newly loaded module, we need some sort of identifier for it which we define as moduleName above. In the actual “Module Specification” section, we have this defined as a constant MODULE_NAME. Finally, we need to tell the application what functions it’s looking for from the library’s export table to acquire a function pointers to, which is denoted by functionName and callbackName for the main module function and callback function respectively. The result from C.load_module will be a pointer to a ModuleFunctions structure that has the desired function pointers should they exist, and if not, we return an error to the callee. Lastly, we add this new InMemoryModule to a module manager within the package so that we can call the module from anywhere in our source code before returning a pointer to the new InMemoryModule.
首先,我们检查一下参数。 我们需要知道该库在内存中的位置,因此需要InMemoryFile。 接下来,如果要将数据从应用程序路由到新加载的模块,则需要为其提供某种标识符,我们在上面将其定义为moduleName。 在实际的“模块规范”部分中,我们将其定义为常量MODULE_NAME。 最后,我们需要告诉应用程序要从库的导出表中查找什么函数以获取指向函数的指针,分别由主模块函数和回调函数的functionName和callbackName表示。 C.load_module的结果将是一个指向ModuleFunctions结构的指针,该结构具有所需的函数指针(如果存在),否则,我们将错误返回给被调用方。 最后,我们将此新的InMemoryModule添加到包中的模块管理器中,以便在返回指向新的InMemoryModule的指针之前,可以从源代码中的任何位置调用该模块。
The load_module function defined in memlibrary.h is defined as follows:
memlibrary.h中定义的load_module函数定义如下:
The function takes three arguments: the path to the library to be loaded (libraryPath), the main module function name (functionName), and the module callback should it require more data from the application core (callbackName). On line 7 we load and acquire a handle to the library using the dlopen function (wrapped by load_library here), and allocate a new structure to hold our function pointers from within that library. We then retrieve our function pointers using wrappers to the dlsym command (get_main_export and get_main_callback_export) and populate the structure before returning the resultant ModuleFunctions structure.
该函数包含三个参数:要加载的库的路径(libraryPath),主模块函数名称(functionName),以及需要从应用程序核心获取更多数据的模块回调(callbackName)。 在第7行,我们使用dlopen函数(在这里由load_library包装)加载并获取库的句柄,并分配一个新结构来保存该库中的函数指针。 然后,我们使用包装器检索dlsym命令的函数指针(get_main_export和get_main_callback_export),并在返回结果ModuleFunctions结构之前填充该结构。
概念证明 (The Proof of Concept)
Piecing all of the above together, our main application logic ends up looking like the following:
将以上所有内容结合在一起,我们的主要应用程序逻辑最终如下所示:
The logic here is relatively straight forward. We first allocate a new file in RAM (line 3) using the memfile package and write our library to that file. In this example, the library gosharedlib.so is the example “helloworld” library defined in the “Module Specification” section (Note: While we’re grabbing this file from disk, you could deliver this byte array through any means you’d like). This library has two exports, helloworld and helloworldCallback, which we tell the memlibrary package to go fetch. The library is returned as the lib variable (line 13), and we can directly invoke it by passing a byte array of data to it (line 19). We can even send data to the library as shown on line 21. Finally, because the package implements a module manager internally, we can invoke the module by its short name anywhere in the main application so long as we know the short name associated with the module as shown on line 23. Compiling and running the application returns the following output:
这里的逻辑相对简单。 我们首先使用memfile包在RAM(第3行)中分配一个新文件,然后将库写入该文件。 在此示例中,库gosharedlib.so是在“模块规范”部分中定义的示例“ helloworld”库(注意:当我们从磁盘上抓取此文件时,可以通过任何方式传递此字节数组)。 该库有两个导出,helloworld和helloworldCallback,我们告诉它们要提取内存包。 该库作为lib变量返回(第13行),我们可以通过将字节数据数组传递给它来直接调用它(第19行)。 我们甚至可以将数据发送到库,如第21行所示。最后,由于该包在内部实现了模块管理器,因此只要知道与该应用程序相关联的简称,我们就可以在主应用程序中的任何地方通过其简称调用该模块。模块,如第23行所示。编译并运行该应用程序将返回以下输出:
结论 (Conclusion)
Software development in the offensive security space is rapid, volatile, and spread out amongst individuals each developing their own applications. By moving towards a modular agent design, we can solve many of the problems of integration we face with most open source agents. New modules can be designed on the fly and integrated without having to redeploy or recompile a new callback. These modules could (in theory) be written in any language a developer chooses so long as they adhere to the specification above, facilitating contributions from disparate individuals with varying degrees of experience. Moving towards a modular specification allows an operator to easily tweak, on the fly, any agent command they so choose. Finally, because the agent core is so lightweight, it becomes extremely difficult to signature or determine if the binary is genuinely malicious or not. While this proof of concept is limited in scope, I believe it to be extendable to a variety of operating systems, and hope that it has at least stimulated the creativity of other developers in our field. If you’d like the full source code for this proof of concept, it can be found at the links below.
进攻性安全领域中的软件开发是快速,易变的,并且分布在每个各自开发自己的应用程序的个人中。 通过转向模块化代理设计,我们可以解决大多数开源代理所面临的许多集成问题。 新模块可以即时设计和集成,而无需重新部署或重新编译新的回调。 这些模块(理论上)可以使用开发人员选择的任何语言编写,只要它们遵守上述规范即可,以促进来自不同经验程度不同的个人的贡献。 朝着模块化规范发展,使操作员可以轻松地即时调整他们选择的任何代理命令。 最后,由于代理程序核心非常轻巧,因此签名或确定二进制文件是否真的是恶意的变得极为困难。 尽管此概念证明的范围有限,但我认为它可以扩展到各种操作系统,并希望它至少激发了我们领域其他开发人员的创造力。 如果您想要此概念证明的完整源代码,请参见下面的链接。
翻译自: https://posts.specterops.io/malware-development-pt-1-dynamic-module-loading-in-go-1121f07f3a5a
pytorch加载pt模型