将EXE文件加载到另一个进程的内存地址空间中。

huangapple go评论89阅读模式
英文:

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(&quot;...\\helper.exe&quot;)
if err != nil {
}
//fmt.Print(file)
f, err := os.Create(&quot;...\\fileInByte.txt&quot;)
if err != nil {
}
defer f.Close()
_, err = f.Write([]byte(file))
file2, err2 := ioutil.ReadFile(&quot;...\\fileInByte.txt&quot;)
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(&quot;...\\fileInByte.txt&quot;, std::ios::binary);
std::vector&lt;unsigned char&gt; buffer(std::istreambuf_iterator&lt;char&gt;(input), {});
unsigned char* my_payload;
my_payload = &amp;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++程序中的有效载荷,对吗?

那么你不需要使用goc++。实用程序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 (
&quot;fmt&quot;
&quot;io&quot;
&quot;os&quot;
&quot;path&quot;
)
func main() {
if len(os.Args) == 1 {
die(&quot;Missing file name&quot;)
}
fname := os.Args[1]
file, err := os.Open(fname)
if err != nil {
die_f(&quot;Failed to open %s: %s&quot;, 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(&quot;Error reading %s: %s&quot;, fname, err.Error())
}
}
// finish the previous line
if !first {
fmt.Println(&quot;,&quot;)
}
// Print the first column
fmt.Printf(&quot;0x%02x&quot;, buf[0])
// Print other columns
for i := 1; i &lt; n; i++ {
fmt.Printf(&quot;, 0x%02x&quot;, buf[i])
}
first = false
}
fmt.Println()
}
func usage() {
progname := path.Base(os.Args[0])
fmt.Fprintf(os.Stderr, &quot;%s FILE\n\n&quot;, progname)
fmt.Fprintf(os.Stderr, &quot;Prints the file as C-style hex array\n&quot;)
}
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头。

在互联网上可以找到许多代码和库,既有外部的也有内部的。

相关链接

  1. MemoryModule库:用于内部手动映射,支持EXE,我用它来解决这个问题。
  2. 将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.

  1. MemoryModule library: For manual mapping internally, supports EXEs, I used this to solve this exact problem.
  2. Load EXE as DLL: Mission Possible

huangapple
  • 本文由 发表于 2022年11月6日 13:09:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/74333437.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定