1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"syscall"
"unsafe"
pasepe "github.com/Binject/debug/pe"
)
const (
FILE_DEVICE_UNKNOWN = 0x00000022
FILE_ANY_ACCESS = 0x00000000
METHOD_IN_DIRECT = 1
METHOD_OUT_DIRECT = 2
GENERIC_ALL = 0x10000000
FILE_ATTRIBUTE_SYSTEM = 0x00000004
OPEN_EXISTING = 3
INVALID_HANDLE_VALUE = ^uintptr(0)
)
/*
定义一个struct存储SSDT的函数的相关信息,序号,应用层地址,内核层地址,函数名称,函数参数个数
*/
type ssdtEntry struct {
Index uint32
AppAddress uint64
KernelAddr uint64
Name string
ParamsNum uint64
}
var ssdtTables []ssdtEntry
func CTL_CODE(DeviceType, Function, Method, Access uint32) uint32 {
return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method)
}
func call_for_ssdt() []byte {
kernel32 := syscall.NewLazyDLL("kernel32.dll")
createFileW := kernel32.NewProc("CreateFileW")
getLastError := kernel32.NewProc("GetLastError")
closeHandle := kernel32.NewProc("CloseHandle")
deviceIoControl := kernel32.NewProc("DeviceIoControl")
// 确保设备路径是宽字符字符串
devicePath := `\\.\newDevice`
devicePathPtr := syscall.StringToUTF16Ptr(devicePath)
// 打开设备
handle, _, callErr := createFileW.Call(
uintptr(unsafe.Pointer(devicePathPtr)),
GENERIC_ALL,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM,
0,
)
// 检查句柄是否有效
if handle == INVALID_HANDLE_VALUE {
// 打印错误码
lastError, _, _ := getLastError.Call()
fmt.Printf("打开设备失败,错误码:%d\n", lastError)
// 打印调用错误
if callErr != nil {
fmt.Println("调用 CreateFileW 时发生错误:", callErr.Error())
}
return nil
}
defer closeHandle.Call(handle)
// 分配缓冲区
buff := make([]byte, 0x100000)
realRead := uint32(0)
// 调用 DeviceIoControl
success, _, callErr := deviceIoControl.Call(
handle,
uintptr(CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)),
0,
0,
uintptr(unsafe.Pointer(&buff[0])),
0x100000,
uintptr(unsafe.Pointer(&realRead)),
0,
)
// 检查 DeviceIoControl 是否成功
if success == 0 {
// 打印错误码
lastError, _, _ := getLastError.Call()
fmt.Printf("DeviceIoControl 调用失败,错误码:%d\n", lastError)
// 打印调用错误
if callErr != nil {
fmt.Println("调用 DeviceIoControl 时发生错误:", callErr.Error())
}
return nil
}
return buff
}
func get_exports() bool {
hProcess, _ := syscall.GetCurrentProcess()
var ntdll = syscall.NewLazyDLL("ntdll.dll")
var NtQueryInformationProcessProc = ntdll.NewProc("NtQueryInformationProcess")
type UNICODE_STRING struct {
Length uint16
MaximumLength uint16
Buffer *uint16
}
// LIST_ENTRY结构体定义,根据Windows的定义
type LIST_ENTRY struct {
Flink *LIST_ENTRY
Blink *LIST_ENTRY
}
// _LDR_DATA_TABLE_ENTRY结构体定义,根据Windows的定义
type _LDR_DATA_TABLE_ENTRY struct {
InLoadOrderLinks LIST_ENTRY // LIST_ENTRY结构体指针
InMemoryOrderLinks LIST_ENTRY // LIST_ENTRY结构体指针
InInitializationOrderLinks LIST_ENTRY // LIST_ENTRY结构体指针
DllBase uintptr
EntryPoint uintptr
SizeOfImage uint32
FullDllName UNICODE_STRING // UNICODE_STRING结构体,此处简化处理为固定长度数组
BaseDllName UNICODE_STRING // 同上,简化处理
}
// _PEB_LDR_DATA结构体定义,根据Windows的定义
type _PEB_LDR_DATA struct {
Length uint32
Initialized uint8
SsHandle uintptr
InLoadOrderModuleList LIST_ENTRY // LIST_ENTRY结构体指针
InMemoryOrderModuleList LIST_ENTRY // LIST_ENTRY结构体指针
InInitializationOrderModuleList LIST_ENTRY // LIST_ENTRY结构体指针
EntryInProgress uintptr
ShutdownInProgress uint8
ShutdownThreadId uintptr
}
// PEB结构体定义,根据Windows的定义
type PEB struct {
BeingDebugged uint32
Mutant uintptr
ImageBaseAddress uintptr
Ldr *_PEB_LDR_DATA // PEB_LDR_DATA结构体的指针
}
// 把_PROCESS_BASIC_INFORMATION 转换为go的struct
type PROCESS_BASIC_INFORMATION struct {
ExitStatus uint32
PebBaseAddress *uint64
AffinityMask uint64
BasePriority int32
UniqueProcessId uint64
InheritedFromUniqueProcessId uint64
}
var ProcessBasicInformation PROCESS_BASIC_INFORMATION
var ReturnLength uint32
ret, _, _ := NtQueryInformationProcessProc.Call(
uintptr(hProcess),
0,
uintptr(unsafe.Pointer(&ProcessBasicInformation)),
uintptr(unsafe.Sizeof(ProcessBasicInformation)),
uintptr(unsafe.Pointer(&ReturnLength)))
fmt.Printf("Return:%d\n", ret)
// 读取PEB结构体
peb := (*PEB)(unsafe.Pointer(ProcessBasicInformation.PebBaseAddress))
fmt.Printf("ImageBaseAddress:0x%x\n", peb.ImageBaseAddress)
fmt.Printf("Ldr:0x%x\n", (unsafe.Pointer(peb.Ldr)))
// 读取_PEB_LDR_DATA结构体,增加类型安全检查
ldrPtr := peb.Ldr
fmt.Printf("Length:%d\n", ldrPtr.Length)
fmt.Printf("Initialized:%d\n", ldrPtr.Initialized)
beginLdr := &(ldrPtr.InLoadOrderModuleList)
// 名称:地址的map,地址map
exportsName := make(map[string]uint32)
exportsAddress := make(map[uint32]string)
// 读取InLoadOrderModuleList结构体
listEntry := (*_LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(ldrPtr.InLoadOrderModuleList.Flink))
for listEntry != nil && &(listEntry.InLoadOrderLinks) != beginLdr {
fmt.Printf("DllBase:0x%x\n", listEntry.DllBase)
fmt.Printf("EntryPoint:0x%x\n", listEntry.EntryPoint)
fmt.Printf("SizeOfImage:%d\n", listEntry.SizeOfImage)
// 使用 unsafe.Slice 创建一个适当大小的 uint16 切片
FullDllName := unsafe.Slice((*uint16)(unsafe.Pointer(listEntry.FullDllName.Buffer)), listEntry.FullDllName.Length/2)
fmt.Printf("FullDllName:%s\n", syscall.UTF16ToString(FullDllName))
baseDllName := unsafe.Slice((*uint16)(unsafe.Pointer(listEntry.BaseDllName.Buffer)), listEntry.BaseDllName.Length/2)
fmt.Printf("BaseDllName:%s\n", syscall.UTF16ToString((baseDllName)))
// 非ntdll.dll的模块不解析
if syscall.UTF16ToString(baseDllName) != "ntdll.dll" {
listEntry = (*_LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(listEntry.InLoadOrderLinks.Flink))
continue
}
//解析dll
// 将uintptr转换为[]byte
dllBytes := unsafe.Slice((*uint8)(unsafe.Pointer(listEntry.DllBase)), listEntry.SizeOfImage)
// 使用通过读取内存的方法获取PE文件信息
ntdll, err := pasepe.NewFileFromMemory(bytes.NewReader(dllBytes))
// 遍历exports
if err != nil {
log.Fatalf("Failed to parse PE file: %v", err)
return false
}
// 解析导出表
if exp, err := ntdll.Exports(); err == nil {
fmt.Println("导出函数列表:")
for _, sym := range exp {
// fmt.Printf("Ordinal %d, Name: %s, Address: %d, Forward: %s\n",
// sym.Ordinal, sym.Name, sym.VirtualAddress, sym.Forward)
exportsName[sym.Name] = sym.VirtualAddress
exportsAddress[sym.VirtualAddress] = sym.Name
}
} else {
log.Fatalf("Failed to get exports: %v", err)
}
// 目标:打印一个字节,从NtAccessCheck地址开始,每0x20个字节一组,打印每组的第5个字节
// 其中,每组的前4个字节分别是,0x4c,0x8b,0xd1,0xb8,打印到前四个不是这个四个字节为止
for i := exportsName["NtAccessCheck"]; i < uint32(len(dllBytes)); i += 0x20 {
ordinal := binary.LittleEndian.Uint32(dllBytes[i+4 : i+8])
if dllBytes[i] != 0x4c || dllBytes[i+1] != 0x8b || dllBytes[i+2] != 0xd1 || dllBytes[i+3] != 0xb8 {
if i > uint32(exportsName["NtAccessCheck"]+0x20000) || ordinal == 0xcccccccc {
break
}
i = i + 0x10
}
if ordinal <= 0x500 {
fmt.Printf("函数名称: %s 函数序号:%x 地址:%x\n", exportsAddress[i], ordinal, (uint64)(listEntry.DllBase))
entry := ssdtEntry{}
entry.AppAddress = (uint64)(listEntry.DllBase) + uint64(i)
entry.Index = ordinal
entry.Name = exportsAddress[i]
if len(ssdtTables) <= int(ordinal) {
// 扩展切片长度
ssdtTables = append(ssdtTables, make([]ssdtEntry, int(ordinal)-len(ssdtTables)+1)...)
}
ssdtTables[ordinal] = entry
}
}
listEntry = (*_LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(listEntry.InLoadOrderLinks.Flink))
}
return true
}
func GetSSDT() []ssdtEntry {
// 通过ntdll获取ssdt表的各函数名称
if !get_exports() {
return nil
}
buff := call_for_ssdt()
if buff == nil {
fmt.Println("获取SSDT失败")
return nil
}
// 解析返回数据
begin := (*[1 << 30]uint64)(unsafe.Pointer(&buff[0]))
for i := uint32(0); i < uint32(len(buff))/(2*8); i += 2 {
if begin[i] == 0 {
// fmt.Println("地址为 0,暂停...")
// fmt.Println("按任意键继续...")
// var input string
// fmt.Scanln(&input)
break
} else {
// fmt.Printf("address:%x paramsnum:%x number:%d\n", begin[i], begin[i+1], i/2)
ssdtTables[i/2].KernelAddr = begin[i]
ssdtTables[i/2].ParamsNum = begin[i+1]
}
}
return ssdtTables
}
func main() {
ssdtTable := GetSSDT()
for _, v := range ssdtTable {
fmt.Printf("序号:%d 函数名称: %s 内核地址:%x 应用层地址:%x 参数个数:%d\n", v.Index, v.Name, v.KernelAddr, v.AppAddress, v.ParamsNum)
}
}
|