| ... | ... | @@ -153,14 +153,149 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 153 | 153 | } |
| 154 | 154 | } |
| 155 | 155 | |
| 156 | | pub const CreatePipeError = error{Unexpected}; |
| 156 | pub fn GetCurrentProcess() HANDLE { |
| 157 | const process_pseudo_handle: usize = @bitCast(@as(isize, -1)); |
| 158 | return @ptrFromInt(process_pseudo_handle); |
| 159 | } |
| 160 | |
| 161 | pub fn GetCurrentProcessId() DWORD { |
| 162 | return @truncate(@intFromPtr(teb().ClientId.UniqueProcess)); |
| 163 | } |
| 164 | |
| 165 | pub fn GetCurrentThread() HANDLE { |
| 166 | const thread_pseudo_handle: usize = @bitCast(@as(isize, -2)); |
| 167 | return @ptrFromInt(thread_pseudo_handle); |
| 168 | } |
| 169 | |
| 170 | pub fn GetCurrentThreadId() DWORD { |
| 171 | return @truncate(@intFromPtr(teb().ClientId.UniqueThread)); |
| 172 | } |
| 173 | |
| 174 | pub const CreatePipeError = error{ Unexpected, SystemResources }; |
| 157 | 175 | |
| 176 | var npfs: ?HANDLE = null; |
| 177 | |
| 178 | /// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls. |
| 179 | /// It implements similar behavior to `CreatePipe` and is meant to serve |
| 180 | /// as a direct substitute for that call. |
| 158 | 181 | pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { |
| 159 | | if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) { |
| 160 | | switch (kernel32.GetLastError()) { |
| 161 | | else => |err| return unexpectedError(err), |
| 182 | // Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to: |
| 183 | // |
| 184 | // \??\pipe\Win32Pipes.{pid}.{count} |
| 185 | // |
| 186 | // where `pid` is the process id and count is a incrementing counter. |
| 187 | // The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System |
| 188 | // and use that as the root directory for `NtCreateNamedPipeFile`. |
| 189 | // This object is visible under the NPFS but has no filename attached to it. |
| 190 | // |
| 191 | // This implementation replicates how `CreatePipe` works in modern Windows versions. |
| 192 | const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst); |
| 193 | const dev_handle = opt_dev_handle orelse blk: { |
| 194 | const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\"); |
| 195 | const len: u16 = @truncate(str.len * @sizeOf(u16)); |
| 196 | const name = UNICODE_STRING{ |
| 197 | .Length = len, |
| 198 | .MaximumLength = len, |
| 199 | .Buffer = @constCast(@ptrCast(str)), |
| 200 | }; |
| 201 | const attrs = OBJECT_ATTRIBUTES{ |
| 202 | .ObjectName = @constCast(&name), |
| 203 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| 204 | .RootDirectory = null, |
| 205 | .Attributes = 0, |
| 206 | .SecurityDescriptor = null, |
| 207 | .SecurityQualityOfService = null, |
| 208 | }; |
| 209 | |
| 210 | var iosb: IO_STATUS_BLOCK = undefined; |
| 211 | var handle: HANDLE = undefined; |
| 212 | switch (ntdll.NtCreateFile( |
| 213 | &handle, |
| 214 | GENERIC_READ | SYNCHRONIZE, |
| 215 | @constCast(&attrs), |
| 216 | &iosb, |
| 217 | null, |
| 218 | 0, |
| 219 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 220 | FILE_OPEN, |
| 221 | FILE_SYNCHRONOUS_IO_NONALERT, |
| 222 | null, |
| 223 | 0, |
| 224 | )) { |
| 225 | .SUCCESS => {}, |
| 226 | // Judging from the ReactOS sources this is technically possible. |
| 227 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| 228 | .INVALID_PARAMETER => unreachable, |
| 229 | else => |e| return unexpectedStatus(e), |
| 162 | 230 | } |
| 231 | if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| { |
| 232 | CloseHandle(handle); |
| 233 | break :blk xchg.?; |
| 234 | } else break :blk handle; |
| 235 | }; |
| 236 | |
| 237 | const name = UNICODE_STRING{ .Buffer = null, .Length = 0, .MaximumLength = 0 }; |
| 238 | var attrs = OBJECT_ATTRIBUTES{ |
| 239 | .ObjectName = @constCast(&name), |
| 240 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| 241 | .RootDirectory = dev_handle, |
| 242 | .Attributes = OBJ_CASE_INSENSITIVE, |
| 243 | .SecurityDescriptor = sattr.lpSecurityDescriptor, |
| 244 | .SecurityQualityOfService = null, |
| 245 | }; |
| 246 | if (sattr.bInheritHandle != 0) attrs.Attributes |= OBJ_INHERIT; |
| 247 | |
| 248 | // 120 second relative timeout in 100ns units. |
| 249 | const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100; |
| 250 | var iosb: IO_STATUS_BLOCK = undefined; |
| 251 | var read: HANDLE = undefined; |
| 252 | switch (ntdll.NtCreateNamedPipeFile( |
| 253 | &read, |
| 254 | GENERIC_READ | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, |
| 255 | &attrs, |
| 256 | &iosb, |
| 257 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 258 | FILE_CREATE, |
| 259 | FILE_SYNCHRONOUS_IO_NONALERT, |
| 260 | FILE_PIPE_BYTE_STREAM_TYPE, |
| 261 | FILE_PIPE_BYTE_STREAM_MODE, |
| 262 | FILE_PIPE_QUEUE_OPERATION, |
| 263 | 1, |
| 264 | 4096, |
| 265 | 4096, |
| 266 | @constCast(&default_timeout), |
| 267 | )) { |
| 268 | .SUCCESS => {}, |
| 269 | .INVALID_PARAMETER => unreachable, |
| 270 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| 271 | else => |e| return unexpectedStatus(e), |
| 163 | 272 | } |
| 273 | errdefer CloseHandle(read); |
| 274 | |
| 275 | attrs.RootDirectory = read; |
| 276 | |
| 277 | var write: HANDLE = undefined; |
| 278 | switch (ntdll.NtCreateFile( |
| 279 | &write, |
| 280 | GENERIC_WRITE | SYNCHRONIZE | FILE_READ_ATTRIBUTES, |
| 281 | &attrs, |
| 282 | &iosb, |
| 283 | null, |
| 284 | 0, |
| 285 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 286 | FILE_OPEN, |
| 287 | FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, |
| 288 | null, |
| 289 | 0, |
| 290 | )) { |
| 291 | .SUCCESS => {}, |
| 292 | .INVALID_PARAMETER => unreachable, |
| 293 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| 294 | else => |e| return unexpectedStatus(e), |
| 295 | } |
| 296 | |
| 297 | rd.* = read; |
| 298 | wr.* = write; |
| 164 | 299 | } |
| 165 | 300 | |
| 166 | 301 | pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE { |
| ... | ... | @@ -1050,35 +1185,32 @@ pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 { |
| 1050 | 1185 | return @as(u64, @bitCast(result)); |
| 1051 | 1186 | } |
| 1052 | 1187 | |
| 1053 | | pub fn QueryObjectName( |
| 1054 | | handle: HANDLE, |
| 1055 | | out_buffer: []u16, |
| 1056 | | ) ![]u16 { |
| 1188 | pub fn QueryObjectName(handle: HANDLE, out_buffer: []u16) ![]u16 { |
| 1057 | 1189 | const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong; |
| 1058 | 1190 | |
| 1059 | 1191 | const info = @as(*OBJECT_NAME_INFORMATION, @ptrCast(out_buffer_aligned)); |
| 1060 | | //buffer size is specified in bytes |
| 1192 | // buffer size is specified in bytes |
| 1061 | 1193 | const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(ULONG); |
| 1062 | | //last argument would return the length required for full_buffer, not exposed here |
| 1063 | | const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null); |
| 1064 | | switch (rc) { |
| 1065 | | .SUCCESS => { |
| 1194 | // last argument would return the length required for full_buffer, not exposed here |
| 1195 | return switch (ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null)) { |
| 1196 | .SUCCESS => blk: { |
| 1066 | 1197 | // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0) |
| 1067 | 1198 | // if the object was "unnamed", not sure if this can happen for file handles |
| 1068 | | if (info.Name.MaximumLength == 0) return error.Unexpected; |
| 1199 | if (info.Name.MaximumLength == 0) break :blk error.Unexpected; |
| 1069 | 1200 | // resulting string length is specified in bytes |
| 1070 | 1201 | const path_length_unterminated = @divExact(info.Name.Length, 2); |
| 1071 | | return info.Name.Buffer[0..path_length_unterminated]; |
| 1202 | break :blk info.Name.Buffer.?[0..path_length_unterminated]; |
| 1072 | 1203 | }, |
| 1073 | | .ACCESS_DENIED => return error.AccessDenied, |
| 1074 | | .INVALID_HANDLE => return error.InvalidHandle, |
| 1204 | .ACCESS_DENIED => error.AccessDenied, |
| 1205 | .INVALID_HANDLE => error.InvalidHandle, |
| 1075 | 1206 | // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH), |
| 1076 | 1207 | // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL) |
| 1077 | | .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong, |
| 1078 | | else => |e| return unexpectedStatus(e), |
| 1079 | | } |
| 1208 | .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong, |
| 1209 | else => |e| unexpectedStatus(e), |
| 1210 | }; |
| 1080 | 1211 | } |
| 1081 | | test "QueryObjectName" { |
| 1212 | |
| 1213 | test QueryObjectName { |
| 1082 | 1214 | if (builtin.os.tag != .windows) |
| 1083 | 1215 | return; |
| 1084 | 1216 | |
| ... | ... | @@ -3015,29 +3147,29 @@ pub const OVERLAPPED_ENTRY = extern struct { |
| 3015 | 3147 | |
| 3016 | 3148 | pub const MAX_PATH = 260; |
| 3017 | 3149 | |
| 3018 | | // TODO issue #305 |
| 3019 | | pub const FILE_INFO_BY_HANDLE_CLASS = u32; |
| 3020 | | pub const FileBasicInfo = 0; |
| 3021 | | pub const FileStandardInfo = 1; |
| 3022 | | pub const FileNameInfo = 2; |
| 3023 | | pub const FileRenameInfo = 3; |
| 3024 | | pub const FileDispositionInfo = 4; |
| 3025 | | pub const FileAllocationInfo = 5; |
| 3026 | | pub const FileEndOfFileInfo = 6; |
| 3027 | | pub const FileStreamInfo = 7; |
| 3028 | | pub const FileCompressionInfo = 8; |
| 3029 | | pub const FileAttributeTagInfo = 9; |
| 3030 | | pub const FileIdBothDirectoryInfo = 10; |
| 3031 | | pub const FileIdBothDirectoryRestartInfo = 11; |
| 3032 | | pub const FileIoPriorityHintInfo = 12; |
| 3033 | | pub const FileRemoteProtocolInfo = 13; |
| 3034 | | pub const FileFullDirectoryInfo = 14; |
| 3035 | | pub const FileFullDirectoryRestartInfo = 15; |
| 3036 | | pub const FileStorageInfo = 16; |
| 3037 | | pub const FileAlignmentInfo = 17; |
| 3038 | | pub const FileIdInfo = 18; |
| 3039 | | pub const FileIdExtdDirectoryInfo = 19; |
| 3040 | | pub const FileIdExtdDirectoryRestartInfo = 20; |
| 3150 | pub const FILE_INFO_BY_HANDLE_CLASS = enum(u32) { |
| 3151 | FileBasicInfo = 0, |
| 3152 | FileStandardInfo = 1, |
| 3153 | FileNameInfo = 2, |
| 3154 | FileRenameInfo = 3, |
| 3155 | FileDispositionInfo = 4, |
| 3156 | FileAllocationInfo = 5, |
| 3157 | FileEndOfFileInfo = 6, |
| 3158 | FileStreamInfo = 7, |
| 3159 | FileCompressionInfo = 8, |
| 3160 | FileAttributeTagInfo = 9, |
| 3161 | FileIdBothDirectoryInfo = 10, |
| 3162 | FileIdBothDirectoryRestartInfo = 11, |
| 3163 | FileIoPriorityHintInfo = 12, |
| 3164 | FileRemoteProtocolInfo = 13, |
| 3165 | FileFullDirectoryInfo = 14, |
| 3166 | FileFullDirectoryRestartInfo = 15, |
| 3167 | FileStorageInfo = 16, |
| 3168 | FileAlignmentInfo = 17, |
| 3169 | FileIdInfo = 18, |
| 3170 | FileIdExtdDirectoryInfo = 19, |
| 3171 | FileIdExtdDirectoryRestartInfo = 20, |
| 3172 | }; |
| 3041 | 3173 | |
| 3042 | 3174 | pub const BY_HANDLE_FILE_INFORMATION = extern struct { |
| 3043 | 3175 | dwFileAttributes: DWORD, |
| ... | ... | @@ -3186,6 +3318,25 @@ pub const FILE_ATTRIBUTE_SYSTEM = 0x4; |
| 3186 | 3318 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; |
| 3187 | 3319 | pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; |
| 3188 | 3320 | |
| 3321 | pub const FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1ff; |
| 3322 | pub const FILE_GENERIC_READ = STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE; |
| 3323 | pub const FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE; |
| 3324 | pub const FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE; |
| 3325 | |
| 3326 | // Flags for NtCreateNamedPipeFile |
| 3327 | // NamedPipeType |
| 3328 | pub const FILE_PIPE_BYTE_STREAM_TYPE = 0x0; |
| 3329 | pub const FILE_PIPE_MESSAGE_TYPE = 0x1; |
| 3330 | pub const FILE_PIPE_ACCEPT_REMOTE_CLIENTS = 0x0; |
| 3331 | pub const FILE_PIPE_REJECT_REMOTE_CLIENTS = 0x2; |
| 3332 | pub const FILE_PIPE_TYPE_VALID_MASK = 0x3; |
| 3333 | // CompletionMode |
| 3334 | pub const FILE_PIPE_QUEUE_OPERATION = 0x0; |
| 3335 | pub const FILE_PIPE_COMPLETE_OPERATION = 0x1; |
| 3336 | // ReadMode |
| 3337 | pub const FILE_PIPE_BYTE_STREAM_MODE = 0x0; |
| 3338 | pub const FILE_PIPE_MESSAGE_MODE = 0x1; |
| 3339 | |
| 3189 | 3340 | // flags for CreateEvent |
| 3190 | 3341 | pub const CREATE_EVENT_INITIAL_SET = 0x00000002; |
| 3191 | 3342 | pub const CREATE_EVENT_MANUAL_RESET = 0x00000001; |
| ... | ... | @@ -4151,7 +4302,7 @@ pub const OBJ_VALID_ATTRIBUTES = 0x000003F2; |
| 4151 | 4302 | pub const UNICODE_STRING = extern struct { |
| 4152 | 4303 | Length: c_ushort, |
| 4153 | 4304 | MaximumLength: c_ushort, |
| 4154 | | Buffer: [*]WCHAR, |
| 4305 | Buffer: ?[*]WCHAR, |
| 4155 | 4306 | }; |
| 4156 | 4307 | |
| 4157 | 4308 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; |
| ... | ... | @@ -4176,7 +4327,11 @@ pub const THREAD_BASIC_INFORMATION = extern struct { |
| 4176 | 4327 | }; |
| 4177 | 4328 | |
| 4178 | 4329 | pub const TEB = extern struct { |
| 4179 | | Reserved1: [12]PVOID, |
| 4330 | NtTib: NT_TIB, |
| 4331 | EnvironmentPointer: PVOID, |
| 4332 | ClientId: CLIENT_ID, |
| 4333 | ActiveRpcHandle: PVOID, |
| 4334 | ThreadLocalStoragePointer: PVOID, |
| 4180 | 4335 | ProcessEnvironmentBlock: *PEB, |
| 4181 | 4336 | Reserved2: [399]PVOID, |
| 4182 | 4337 | Reserved3: [1952]u8, |
| ... | ... | @@ -4188,6 +4343,25 @@ pub const TEB = extern struct { |
| 4188 | 4343 | TlsExpansionSlots: PVOID, |
| 4189 | 4344 | }; |
| 4190 | 4345 | |
| 4346 | comptime { |
| 4347 | // Offsets taken from WinDbg info and Geoff Chappell[1] (RIP) |
| 4348 | // [1]: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm |
| 4349 | assert(@offsetOf(TEB, "NtTib") == 0x00); |
| 4350 | if (@sizeOf(usize) == 4) { |
| 4351 | assert(@offsetOf(TEB, "EnvironmentPointer") == 0x1C); |
| 4352 | assert(@offsetOf(TEB, "ClientId") == 0x20); |
| 4353 | assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x28); |
| 4354 | assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x2C); |
| 4355 | assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x30); |
| 4356 | } else if (@sizeOf(usize) == 8) { |
| 4357 | assert(@offsetOf(TEB, "EnvironmentPointer") == 0x38); |
| 4358 | assert(@offsetOf(TEB, "ClientId") == 0x40); |
| 4359 | assert(@offsetOf(TEB, "ActiveRpcHandle") == 0x50); |
| 4360 | assert(@offsetOf(TEB, "ThreadLocalStoragePointer") == 0x58); |
| 4361 | assert(@offsetOf(TEB, "ProcessEnvironmentBlock") == 0x60); |
| 4362 | } |
| 4363 | } |
| 4364 | |
| 4191 | 4365 | pub const EXCEPTION_REGISTRATION_RECORD = extern struct { |
| 4192 | 4366 | Next: ?*EXCEPTION_REGISTRATION_RECORD, |
| 4193 | 4367 | Handler: ?*EXCEPTION_DISPOSITION, |