├── go.mod ├── .gitignore ├── internal ├── helpers │ └── helpers.go ├── user32 │ └── user32.go └── kernel32 │ └── kernel32.go ├── LICENSE ├── README.md ├── .github └── workflows │ └── ci.yaml ├── gomem_windows_amd64.go └── gomem_windows_amd64_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jamesmoriarty/gomem 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | gomem.exe 2 | 3 | # go test -covermode=atomic -coverprofile=coverage -v ./... 4 | coverage 5 | 6 | # go tool cover -html=coverage -o coverage.html 7 | coverage.html 8 | -------------------------------------------------------------------------------- /internal/helpers/helpers.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // PtrToHex converts uintptr to hex string. 9 | func PtrToHex(ptr uintptr) string { 10 | s := fmt.Sprintf("%d", ptr) 11 | n, _ := strconv.Atoi(s) 12 | h := fmt.Sprintf("0x%x", n) 13 | return h 14 | } 15 | -------------------------------------------------------------------------------- /internal/user32/user32.go: -------------------------------------------------------------------------------- 1 | package user32 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var ( 8 | moduser32 = syscall.MustLoadDLL("user32.dll") 9 | procGetAsyncKeyState = moduser32.MustFindProc("GetAsyncKeyState") 10 | ) 11 | 12 | func GetAsyncKeyState(vKey int) uint16 { 13 | ret, _, _ := procGetAsyncKeyState.Call(uintptr(vKey)) 14 | 15 | return uint16(ret) 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 James Moriarty 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoMem 2 | 3 | ![Continuous Integration](https://github.com/jamesmoriarty/gomem/workflows/Continuous%20Integration/badge.svg?branch=master) ![Latest Tag](https://img.shields.io/github/v/tag/jamesmoriarty/gomem.svg?logo=github&label=latest) [![Go Report Card](https://goreportcard.com/badge/github.com/jamesmoriarty/gomem)](https://goreportcard.com/report/github.com/jamesmoriarty/gomem) 4 | 5 | A Go package for manipulating Windows processes. Automated tests manipulate and verify their own process memory via Windows APIs. 6 | 7 | ```go 8 | import "github.com/jamesmoriarty/gomem" 9 | 10 | // Open process with handle. 11 | process, err := gomem.GetOpenProcessFromName("example.exe") 12 | 13 | // Read from process memory. 14 | valuePtr, err := process.ReadUInt32(offsetPtr) 15 | 16 | // Write to process memory. 17 | process.WriteByte(valuePtr, value) 18 | ``` 19 | 20 | ## Build 21 | 22 | ``` 23 | go build 24 | ``` 25 | 26 | ## Test 27 | 28 | ``` 29 | go test 30 | ``` 31 | 32 | ## Docs 33 | 34 | [pkg.go.dev/github.com/jamesmoriarty/gomem](https://pkg.go.dev/github.com/jamesmoriarty/gomem) 35 | 36 | ## Examples 37 | 38 | [github.com/jamesmoriarty/gohack](https://github.com/jamesmoriarty/gohack) 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | lint: 7 | strategy: 8 | matrix: 9 | os: [ 'windows-latest' ] 10 | go: [ '1.16' ] 11 | 12 | runs-on: ${{ matrix.os }} 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - uses: actions/setup-go@v1 18 | with: 19 | go-version: ${{ matrix.go }} 20 | 21 | - run: gofmt -l $(find . -type f -name '*.go') 2>&1 22 | 23 | test: 24 | strategy: 25 | matrix: 26 | os: [ 'windows-latest'] 27 | go: [ '1.15', '1.16' ] 28 | 29 | runs-on: ${{ matrix.os }} 30 | 31 | steps: 32 | - uses: actions/checkout@v2 33 | 34 | - uses: actions/setup-go@v1 35 | with: 36 | go-version: ${{ matrix.go }} 37 | 38 | - run: go test -covermode=atomic -coverprofile=coverage -v ./... 39 | env: 40 | CGO_ENABLED: 1 41 | 42 | build: 43 | strategy: 44 | matrix: 45 | target: 46 | - os: 'windows-latest' 47 | goos: 'windows' 48 | goarch: 'amd64' 49 | go: [ '1.15', '1.16' ] 50 | 51 | runs-on: ${{ matrix.target.os }} 52 | 53 | steps: 54 | - uses: actions/checkout@v2 55 | 56 | - uses: actions/setup-go@v1 57 | with: 58 | go-version: ${{ matrix.go }} 59 | 60 | - run: go build -v . 61 | env: 62 | GOOS: ${{ matrix.target.goos }} 63 | GOARCH: ${{ matrix.target.goarch }} 64 | CGO_ENABLED: 1 -------------------------------------------------------------------------------- /gomem_windows_amd64.go: -------------------------------------------------------------------------------- 1 | package gomem 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/jamesmoriarty/gomem/internal/kernel32" 7 | "github.com/jamesmoriarty/gomem/internal/user32" 8 | ) 9 | 10 | // Process is a struct representing a windows process. 11 | type Process struct { 12 | ID uint32 13 | Name string 14 | Handle uintptr 15 | } 16 | 17 | // GetProcessFromName converts a process name to a Process struct. 18 | func GetProcessFromName(name string) (*Process, error) { 19 | pid, err := kernel32.GetProcessID(name) 20 | 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | process := Process{ID: pid, Name: name} 26 | 27 | return &process, nil 28 | } 29 | 30 | // GetOpenProcessFromName converts a process name to a Process struct with open handle. 31 | func GetOpenProcessFromName(name string) (*Process, error) { 32 | process, err := GetProcessFromName(name) 33 | 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | _, err = process.Open() 39 | 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return process, nil 45 | } 46 | 47 | // Open process handle. 48 | func (p *Process) Open() (uintptr, error) { 49 | handle, err := kernel32.OpenProcess(kernel32.PROCESS_ALL_ACCESS, false, p.ID) 50 | 51 | if err != nil { 52 | return 0, err 53 | } 54 | 55 | p.Handle = handle 56 | 57 | return handle, err 58 | } 59 | 60 | // Read process memory. 61 | func (p *Process) Read(offset uintptr, buffer uintptr, length uintptr) error { 62 | _, err := kernel32.ReadProcessMemory(p.Handle, offset, buffer, length) 63 | 64 | return err 65 | } 66 | 67 | // Read byte from process memory. 68 | func (p *Process) ReadByte(offset uintptr) (byte, error) { 69 | var ( 70 | value byte 71 | valuePtr = (uintptr)(unsafe.Pointer(&value)) 72 | ) 73 | 74 | err := p.Read(offset, valuePtr, unsafe.Sizeof(value)) 75 | 76 | return value, err 77 | } 78 | 79 | // Read uint32 from process memory. 80 | func (p *Process) ReadUInt32(offset uintptr) (uint32, error) { 81 | var ( 82 | value uint32 83 | valuePtr = (uintptr)(unsafe.Pointer(&value)) 84 | ) 85 | 86 | err := p.Read(offset, valuePtr, unsafe.Sizeof(value)) 87 | 88 | return value, err 89 | } 90 | 91 | // Read uint64 from process memory. 92 | func (p *Process) ReadUInt64(offset uintptr) (uint64, error) { 93 | var ( 94 | value uint64 95 | valuePtr = (uintptr)(unsafe.Pointer(&value)) 96 | ) 97 | 98 | err := p.Read(offset, valuePtr, unsafe.Sizeof(value)) 99 | 100 | return value, err 101 | } 102 | 103 | // Write process memory. 104 | func (p *Process) Write(offset uintptr, buffer uintptr, length uintptr) error { 105 | _, err := kernel32.WriteProcessMemory(p.Handle, offset, buffer, length) 106 | 107 | return err 108 | } 109 | 110 | // Write byte to process memory. 111 | func (p *Process) WriteByte(offset uintptr, value byte) error { 112 | var ( 113 | valuePtr = (uintptr)(unsafe.Pointer(&value)) 114 | ) 115 | 116 | return p.Write(offset, valuePtr, unsafe.Sizeof(value)) 117 | } 118 | 119 | // GetModule address. 120 | func (p *Process) GetModule(name string) (uintptr, error) { 121 | ptr, err := kernel32.GetModule(name, p.ID) 122 | 123 | return ptr, err 124 | } 125 | 126 | // IsKeyDown https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes 127 | func IsKeyDown(v int) bool { 128 | return user32.GetAsyncKeyState(v) > 0 129 | } 130 | -------------------------------------------------------------------------------- /gomem_windows_amd64_test.go: -------------------------------------------------------------------------------- 1 | package gomem 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | "unsafe" 8 | ) 9 | 10 | func TestGetProcessFromName(t *testing.T) { 11 | name := executableName() 12 | 13 | process, err := GetProcessFromName(name) 14 | 15 | if err != nil { 16 | t.Errorf(err.Error()) 17 | } 18 | 19 | if process.ID == 0 { 20 | t.Errorf("unexpected process id") 21 | } 22 | 23 | if process.Name != name { 24 | t.Errorf("unexpected process name") 25 | } 26 | } 27 | 28 | func TestProcessOpen(t *testing.T) { 29 | name := executableName() 30 | 31 | process, _ := GetProcessFromName(name) 32 | 33 | handle, err := process.Open() 34 | 35 | if err != nil { 36 | t.Errorf(err.Error()) 37 | } 38 | 39 | if handle == 0 { 40 | t.Errorf("unexpected handle id") 41 | } 42 | } 43 | 44 | func TestProcessReadByte(t *testing.T) { 45 | name := executableName() 46 | 47 | var value = (byte)(0x42) 48 | valuePtr := (uintptr)(unsafe.Pointer(&value)) 49 | 50 | process, err := GetOpenProcessFromName(name) 51 | 52 | if err != nil { 53 | t.Errorf(err.Error()) 54 | } 55 | 56 | assertValue, err := process.ReadByte(valuePtr) 57 | 58 | if err != nil { 59 | t.Errorf(err.Error()) 60 | } 61 | 62 | if value != assertValue { 63 | t.Errorf("unexpected value") 64 | } 65 | } 66 | 67 | func TestProcessReadUInt32(t *testing.T) { 68 | name := executableName() 69 | 70 | var value = (uint32)(42) 71 | valuePtr := (uintptr)(unsafe.Pointer(&value)) 72 | 73 | process, err := GetOpenProcessFromName(name) 74 | 75 | if err != nil { 76 | t.Errorf(err.Error()) 77 | } 78 | 79 | assertValue, err := process.ReadUInt32(valuePtr) 80 | 81 | if err != nil { 82 | t.Errorf(err.Error()) 83 | } 84 | 85 | if value != assertValue { 86 | t.Errorf("unexpected value") 87 | } 88 | } 89 | 90 | func TestProcessReadUInt64(t *testing.T) { 91 | name := executableName() 92 | 93 | var value = (uint64)(42) 94 | valuePtr := (uintptr)(unsafe.Pointer(&value)) 95 | 96 | process, err := GetOpenProcessFromName(name) 97 | 98 | if err != nil { 99 | t.Errorf(err.Error()) 100 | } 101 | 102 | assertValue, err := process.ReadUInt64(valuePtr) 103 | 104 | if err != nil { 105 | t.Errorf(err.Error()) 106 | } 107 | 108 | if value != assertValue { 109 | t.Errorf("unexpected value") 110 | } 111 | } 112 | 113 | func TestProcessWriteByte(t *testing.T) { 114 | name := executableName() 115 | 116 | var ( 117 | value = (byte)(0x42) 118 | valuePtr = (uintptr)(unsafe.Pointer(&value)) 119 | newValue = (byte)(0x43) 120 | ) 121 | 122 | process, err := GetOpenProcessFromName(name) 123 | 124 | if err != nil { 125 | t.Errorf(err.Error()) 126 | } 127 | 128 | err = process.WriteByte(valuePtr, newValue) 129 | 130 | if err != nil { 131 | t.Errorf(err.Error()) 132 | } 133 | 134 | if value != newValue { 135 | t.Errorf("unexpected value") 136 | } 137 | } 138 | 139 | func TestGetModuleNotFound(t *testing.T) { 140 | name := executableName() 141 | 142 | process, err := GetOpenProcessFromName(name) 143 | 144 | if err != nil { 145 | t.Errorf(err.Error()) 146 | } 147 | 148 | ptr, err := process.GetModule("unknown.dll") 149 | 150 | if err.Error() != "module not found" { 151 | t.Errorf(err.Error()) 152 | } 153 | 154 | if (ptr) == 0 { 155 | t.Errorf("unexpected value") 156 | } 157 | } 158 | 159 | func TestIsKeyDown(t *testing.T) { 160 | value := IsKeyDown(0x20) // https://docs.microsoft.com/en-gb/windows/win32/inputdev/virtual-key-codes 161 | 162 | if value != false { 163 | t.Errorf("unexpected value") 164 | } 165 | } 166 | 167 | func executableName() string { 168 | path, _ := os.Executable() 169 | 170 | return filepath.Base(path) 171 | } 172 | -------------------------------------------------------------------------------- /internal/kernel32/kernel32.go: -------------------------------------------------------------------------------- 1 | package kernel32 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | const ( 11 | MAX_MODULE_NAME32 = 255 12 | MAX_PATH = 260 13 | TH32CS_SNAPHEAPLIST = 0x00000001 14 | TH32CS_SNAPPROCESS = 0x00000002 15 | TH32CS_SNAPTHREAD = 0x00000004 16 | TH32CS_SNAPMODULE = 0x00000008 17 | TH32CS_SNAPMODULE32 = 0x00000010 18 | TH32CS_INHERIT = 0x80000000 19 | TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD 20 | STANDARD_RIGHTS_REQUIRED = 0x000F 21 | SYNCHRONIZE = 0x00100000 22 | PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xffff 23 | ) 24 | 25 | // type ( 26 | // BOOL int32 27 | // DWORD uint32 28 | // ULONG_PTR uintptr 29 | // HANDLE uintptr 30 | // LPVOID unsafe.Pointer 31 | // LPCVOID unsafe.Pointer 32 | // SIZE_T uintptr 33 | // HMODULE uintptr 34 | // BYTE byte 35 | // ) 36 | 37 | var ( 38 | kernel32 = syscall.MustLoadDLL("kernel32.dll") 39 | procCloseHandle = kernel32.MustFindProc("CloseHandle") 40 | procCreateToolhelp32Snapshot = kernel32.MustFindProc("CreateToolhelp32Snapshot") 41 | procGetLastError = kernel32.MustFindProc("GetLastError") 42 | procGetModuleHandle = kernel32.MustFindProc("GetModuleHandleW") 43 | procProcess32First = kernel32.MustFindProc("Process32First") 44 | procProcess32Next = kernel32.MustFindProc("Process32Next") 45 | procModule32First = kernel32.MustFindProc("Module32First") 46 | procModule32Next = kernel32.MustFindProc("Module32Next") 47 | procOpenProcess = kernel32.MustFindProc("OpenProcess") 48 | procReadProcessMemory = kernel32.MustFindProc("ReadProcessMemory") 49 | procWriteProcessMemory = kernel32.MustFindProc("WriteProcessMemory") 50 | psapi = syscall.MustLoadDLL("psapi.dll") //kern32 didnt work 51 | procEnumProcessModules = psapi.MustFindProc("EnumProcessModules") 52 | ) 53 | 54 | // https://msdn.microsoft.com/9e2f7345-52bf-4bfc-9761-90b0b374c727 55 | type ProcessEntry32 struct { 56 | DwSize uint32 57 | CntUsage uint32 58 | Th32ProcessID uint32 59 | Th32DefaultHeapID uintptr 60 | Th32ModuleID uint32 61 | CntThreads uint32 62 | Th32ParentProcessID uint32 63 | PcPriClassBase uint32 64 | DwFlags uint32 65 | SzExeFile [MAX_PATH]uint8 66 | } 67 | 68 | // https://msdn.microsoft.com/305fab35-625c-42e3-a434-e2513e4c8870 69 | type ModuleEntry32 struct { 70 | DwSize uint32 71 | Th32ModuleID uint32 72 | Th32ProcessID uint32 73 | GlblcntUsage uint32 74 | ProccntUsage uint32 75 | ModBaseAddr *uintptr 76 | ModBaseSize uint32 77 | HModule uintptr 78 | SzModule [MAX_MODULE_NAME32 + 1]uint8 79 | SzExePath [MAX_PATH]uint8 80 | } 81 | 82 | // https://msdn.microsoft.com/8774e145-ee7f-44de-85db-0445b905f986 83 | func ReadProcessMemory(hProcess uintptr, lpBaseAddress uintptr, lpBuffer uintptr, nSize uintptr) (uintptr, error) { 84 | ret, _, err := procReadProcessMemory.Call( 85 | uintptr(hProcess), 86 | uintptr(lpBaseAddress), 87 | uintptr(lpBuffer), 88 | uintptr(nSize), 89 | 0, 90 | ) 91 | 92 | if err.Error() != "The operation completed successfully." { 93 | return 0, err 94 | } 95 | 96 | return ret, nil 97 | } 98 | 99 | // https://msdn.microsoft.com/9cd91f1c-58ce-4adc-b027-45748543eb06 100 | func WriteProcessMemory(hProcess uintptr, lpBaseAddress uintptr, lpBuffer uintptr, nSize uintptr) (uintptr, error) { 101 | ret, _, err := procWriteProcessMemory.Call( 102 | uintptr(hProcess), 103 | uintptr(lpBaseAddress), 104 | uintptr(lpBuffer), 105 | uintptr(nSize), 106 | ) 107 | 108 | if err.Error() != "The operation completed successfully." { 109 | return 0, err 110 | } 111 | 112 | return ret, nil 113 | } 114 | 115 | // https://msdn.microsoft.com/8f695c38-19c4-49e4-97de-8b64ea536cb1 116 | func OpenProcess(dwDesiredAccess uint32, bInheritHandle bool, dwProcessId uint32) (uintptr, error) { 117 | inHandle := 0 118 | if bInheritHandle { 119 | inHandle = 1 120 | } 121 | 122 | ret, _, _ := procOpenProcess.Call( 123 | uintptr(dwDesiredAccess), 124 | uintptr(inHandle), 125 | uintptr(dwProcessId), 126 | ) 127 | 128 | if ret == 0 { 129 | return 0, errors.New("failed to open process") 130 | } 131 | 132 | return uintptr(ret), nil 133 | } 134 | 135 | func GetModule(module string, PID uint32) (uintptr, error) { 136 | var ( 137 | me32 ModuleEntry32 138 | snap uintptr 139 | szModule string 140 | ) 141 | 142 | snap = createToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32, PID) 143 | me32.DwSize = uint32(unsafe.Sizeof(me32)) 144 | 145 | for ok := module32First(snap, &me32); ok; ok = module32Next(snap, &me32) { 146 | szModule = parseint8(me32.SzModule[:]) 147 | 148 | if szModule == module { 149 | return (uintptr)(unsafe.Pointer(me32.ModBaseAddr)), nil 150 | } 151 | } 152 | 153 | return (uintptr)(unsafe.Pointer(me32.ModBaseAddr)), errors.New("module not found") 154 | } 155 | 156 | func GetProcessID(process string) (uint32, error) { 157 | var ( 158 | handle uintptr 159 | pe32 ProcessEntry32 160 | szExeFile string 161 | ) 162 | 163 | handle = createToolhelp32Snapshot(TH32CS_SNAPALL, 0) 164 | pe32.DwSize = uint32(unsafe.Sizeof(pe32)) 165 | 166 | for ok := process32First(handle, &pe32); ok; ok = process32Next(handle, &pe32) { 167 | szExeFile = parseint8(pe32.SzExeFile[:]) 168 | 169 | if szExeFile == process { 170 | return pe32.Th32ProcessID, nil 171 | } 172 | } 173 | 174 | return 0, errors.New("pid not found") 175 | } 176 | 177 | // https://msdn.microsoft.com/df643c25-7558-424c-b187-b3f86ba51358 178 | func createToolhelp32Snapshot(dwFlags uintptr, th32ProcessID uint32) uintptr { 179 | ret, _, _ := procCreateToolhelp32Snapshot.Call( 180 | uintptr(dwFlags), 181 | uintptr(th32ProcessID), 182 | ) 183 | 184 | return uintptr(ret) 185 | } 186 | 187 | // https://msdn.microsoft.com/097790e8-30c2-4b00-9256-fa26e2ceb893 188 | func process32First(hSnapshot uintptr, pe *ProcessEntry32) bool { 189 | ret, _, _ := procProcess32First.Call( 190 | uintptr(hSnapshot), 191 | uintptr(unsafe.Pointer(pe)), 192 | ) 193 | 194 | return ret != 0 195 | } 196 | 197 | // https://msdn.microsoft.com/843a95fd-27ae-4215-83d0-82fc402b82b6 198 | func process32Next(hSnapshot uintptr, pe *ProcessEntry32) bool { 199 | ret, _, _ := procProcess32Next.Call( 200 | uintptr(hSnapshot), 201 | uintptr(unsafe.Pointer(pe)), 202 | ) 203 | 204 | return ret != 0 205 | } 206 | 207 | // https://msdn.microsoft.com/bb41cab9-13a1-469d-bf76-68c172e982f6 208 | func module32First(hSnapshot uintptr, me *ModuleEntry32) bool { 209 | ret, _, _ := procModule32First.Call( 210 | uintptr(hSnapshot), 211 | uintptr(unsafe.Pointer(me)), 212 | ) 213 | 214 | return ret != 0 215 | } 216 | 217 | // https://msdn.microsoft.com/88ec1af4-bae7-4cd7-b830-97a98fb337f4 218 | func module32Next(hSnapshot uintptr, me *ModuleEntry32) bool { 219 | ret, _, _ := procModule32Next.Call( 220 | uintptr(hSnapshot), 221 | uintptr(unsafe.Pointer(me)), 222 | ) 223 | 224 | return ret != 0 225 | } 226 | 227 | // https://msdn.microsoft.com/9b84891d-62ca-4ddc-97b7-c4c79482abd9 228 | func closeHandle(hObject uintptr) bool { 229 | ret, _, _ := procCloseHandle.Call( 230 | uintptr(hObject), 231 | ) 232 | 233 | return ret != 0 234 | } 235 | 236 | func parseint8(arr []uint8) string { 237 | n := bytes.Index(arr, []uint8{0}) 238 | 239 | return string(arr[:n]) 240 | } 241 | --------------------------------------------------------------------------------