英文:
Load EXE file into memory in the address space of another process
问题
我有一个exe文件,我想要获取它的十六进制字节,将其加载到另一个进程的内存中,然后执行它(类似进程注入)。我想要的几乎就是这篇文章中实现的内容。这篇文章中的负载是计算器软件,它运行良好,但我想要将其替换为任何我想要的exe文件。简而言之,我应该如何进行这种exe到十六进制的转换,以便在另一个进程上下文中执行它?
像从十六进制编辑器和调试器中复制exe的十六进制、在线exe到十六进制转换器以及使用C++指令进行转换这样的方法对我来说都不成功。这是我用Go语言编写的代码:
file, err := ioutil.ReadFile("...\\helper.exe")
if err != nil {
}
//fmt.Print(file)
f, err := os.Create("...\\fileInByte.txt")
if err != nil {
}
defer f.Close()
_, err = f.Write([]byte(file))
file2, err2 := ioutil.ReadFile("...\\fileInByte.txt")
if err2 != nil {
}
fmt.Print(file2)
这是C++代码的第一条指令,用于加载fileInByte.txt的十六进制字节以进行进程注入:
std::ifstream input("...\\fileInByte.txt", std::ios::binary);
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(input), {});
unsigned char* my_payload;
my_payload = &buffer[0];
unsigned int my_payload_len = sizeof(my_payload);
...
...
...
// 为远程进程分配内存缓冲区
rb = VirtualAllocEx(ph, NULL, my_payload_len, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 将负载写入内存缓冲区
if(!WriteProcessMemory(ph, rb, my_payload, my_payload_len, NULL))
英文:
I have an exe file and I want to have its hex bytes to load it in the memory of another process and then execute it (something like process injection). What I want is almost what this post implements. The payload in this post in calculator software and it works well, but I want to substitute it with each exe file that I want. In short, how can I do this exe to hex conversion in such a way that it can be executed in another process context?
Methods like copying hex of exe from hex editors and debugger, online exe to hex converters and also using c++ instructions to do this conversion was not successful for me. This is the code I wrote in Go language:
file, err := ioutil.ReadFile("...\\helper.exe")
if err != nil {
}
//fmt.Print(file)
f, err := os.Create("...\\fileInByte.txt")
if err != nil {
}
defer f.Close()
_, err = f.Write([]byte(file))
file2, err2 := ioutil.ReadFile("...\\fileInByte.txt")
if err2 != nil {
}
fmt.Print(file2)
And this is c++ code first instructions to load fileInByte.txt hex bytes for process injectio:
std::ifstream input("...\\fileInByte.txt", std::ios::binary);
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(input), {});
unsigned char* my_payload;
my_payload = &buffer[0];
unsigned int my_payload_len = sizeof(my_payload);
...
...
...
// allocate memory buffer for remote process
rb = VirtualAllocEx(ph, NULL, my_payload_len, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// write payload to memory buffer
if(!WriteProcessMemory(ph, rb, my_payload, my_payload_len, NULL))
答案1
得分: 1
所以,你需要将可执行文件打印为C风格的十六进制数组,因为你需要替换C++程序中的有效载荷,对吗?
那么你不需要使用go
或c++
。实用程序xxd -i
可以完成这个任务:xxd -i super_duper_kill_em_all_malware.exe
会打印出super_duper_kill_em_all_malware.exe
的十六进制数组。
但是如果对你来说太复杂了,这里有一个简单的Go程序几乎可以实现相同的功能:
package main
import (
"fmt"
"io"
"os"
"path"
)
func main() {
if len(os.Args) == 1 {
die("缺少文件名")
}
fname := os.Args[1]
file, err := os.Open(fname)
if err != nil {
die_f("无法打开%s:%s", fname, err.Error())
}
defer file.Close()
// 每行打印16个字节
buf := make([]byte, 16)
first := true
for {
n, err := io.ReadFull(file, buf)
if err != nil {
if err == io.EOF {
break
}
if err != io.ErrUnexpectedEOF {
file.Close()
die_f("读取%s时出错:%s", fname, err.Error())
}
}
// 结束上一行
if !first {
fmt.Println(",")
}
// 打印第一列
fmt.Printf("0x%02x", buf[0])
// 打印其他列
for i := 1; i < n; i++ {
fmt.Printf(", 0x%02x", buf[i])
}
first = false
}
fmt.Println()
}
func usage() {
progname := path.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "%s FILE\n\n", progname)
fmt.Fprintf(os.Stderr, "将文件打印为C风格的十六进制数组\n")
}
func die(msg string) {
fmt.Fprintln(os.Stderr, msg)
fmt.Fprintln(os.Stderr)
usage()
os.Exit(1)
}
func die_f(format string, vals ...interface{}) {
fmt.Fprintf(os.Stderr, format, vals...)
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr)
usage()
os.Exit(1)
}
示例:go.mod
的C风格十六进制数组
$ go run . go.mod
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6f,
0x72, 0x67, 0x0a, 0x0a, 0x67, 0x6f, 0x20, 0x31, 0x2e, 0x31, 0x39, 0x0a
(这是go.mod
文件的内容:
module example.org
go 1.19
)
英文:
So, you need to print an executable as a C-style hex array because you need to replace the payload in the C++ program, right?
Then you don't need go
or c++
for that. The utility xxd -i
does the trick: xxd -i super_duper_kill_em_all_malware.exe
prints the hex array of super_duper_kill_em_all_malware.exe
But if it is too complicated for you, here is a simple Go program that does almost the same:
package main
import (
"fmt"
"io"
"os"
"path"
)
func main() {
if len(os.Args) == 1 {
die("Missing file name")
}
fname := os.Args[1]
file, err := os.Open(fname)
if err != nil {
die_f("Failed to open %s: %s", fname, err.Error())
}
defer file.Close()
// Print 16 bytes in a row
buf := make([]byte, 16)
first := true
for {
n, err := io.ReadFull(file, buf)
if err != nil {
if err == io.EOF {
break
}
if err != io.ErrUnexpectedEOF {
file.Close()
die_f("Error reading %s: %s", fname, err.Error())
}
}
// finish the previous line
if !first {
fmt.Println(",")
}
// Print the first column
fmt.Printf("0x%02x", buf[0])
// Print other columns
for i := 1; i < n; i++ {
fmt.Printf(", 0x%02x", buf[i])
}
first = false
}
fmt.Println()
}
func usage() {
progname := path.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "%s FILE\n\n", progname)
fmt.Fprintf(os.Stderr, "Prints the file as C-style hex array\n")
}
func die(msg string) {
fmt.Fprintln(os.Stderr, msg)
fmt.Fprintln(os.Stderr)
usage()
os.Exit(1)
}
func die_f(format string, vals ...any) {
fmt.Fprintf(os.Stderr, format, vals...)
fmt.Fprintln(os.Stderr)
fmt.Fprintln(os.Stderr)
usage()
os.Exit(1)
}
Example: C-style hex array of go.mod
$ go run . go.mod
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6f,
0x72, 0x67, 0x0a, 0x0a, 0x67, 0x6f, 0x20, 0x31, 0x2e, 0x31, 0x39, 0x0a
(here is what's inside go.mod
:
module example.org
go 1.19
)
答案2
得分: 1
所以你想进行DLL注入,但是使用EXE,对吗?
LoadLibrary方法
在注入DLL时,通常会在目标进程中创建一个远程线程,调用LoadLibrary
来加载你的DLL,有趣的是,LoadLibrary
可以加载EXE文件。
DLL和EXE之间的主要区别在于DLL在其PE特征中设置了IMAGE_FILE_DLL
标志,因此当调用LoadLibrary
时,它会尝试将DLL映射到内存中,修复其IAT表并调用其入口点。
当对EXE使用LoadLibrary
时,它没有IMAGE_FILE_DLL
标志,因此它只会被映射到内存中,IAT表保持不变,其入口点永远不会被调用。
你可以手动在EXE的特征中设置IMAGE_FILE_DLL
,然后加载它,但由于DLL和EXE之间的差异,它将无法调用入口点。一个解决方法是完全移除PE头中的入口点,并手动调用它。
手动映射方法
手动映射的思路是摆脱LoadLibrary
,尝试手动将DLL映射到内存空间,修复其IAT,并调用其入口点-基本上重新创建LoadLibrary
函数。当然,通过手动映射,我们可以避免像上面描述的那样修改PE头。
在互联网上可以找到许多代码和库,既有外部的也有内部的。
相关链接
- MemoryModule库:用于内部手动映射,支持EXE,我用它来解决这个问题。
- 将EXE作为DLL加载:可能的任务
英文:
So you want to do DLL Injection, but with EXE, right?
LoadLibrary approach
When injecting a DLL, what you would normally do is create a remote thread in the target process that calls LoadLibrary
to load your DLL, interestingly, LoadLibrary
can load EXEs.
The main difference between DLLs and EXEs is that DLLs have the flag IMAGE_FILE_DLL
set in its PE characteristics, so when LoadLibrary
is called, it will try to map the DLL into memory, fix its IAT table and call its entry point.
When using LoadLibrary
on an EXE, it doesn't have the flag IMAGE_FILE_DLL
so it only gets mapped into memory, the IAT is left untouched and its entry point is never called.
You could manually set the IMAGE_FILE_DLL
in an EXE characteristics and load it, but it would fail to call the entry point because of the differences between DLLs and EXEs. A workaround is to remove the entry point in the PE header entirely and manually call it.
Manual mapping approach
The idea of Manual mapping is to get rid of LoadLibrary
, and try to manually map our DLL into the memory space, fix its IAT, and call its entry point - basically recreating the LoadLibrary
function. Of course, by manual mapping, we could avoid patching the PE header as described above.
There are plenty of codes and libraries found on the internet, both external and internal.
Related links
- MemoryModule library: For manual mapping internally, supports EXEs, I used this to solve this exact problem.
- Load EXE as DLL: Mission Possible
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论