Add SetupAPI direct device node driver install method to test tool

Method 3 uses SetupDiGetClassDevs + SetupDiEnumDeviceInfo to find the
exact device node, then SetupDiBuildDriverInfoList + DIF_SELECTBESTCOMPATDRV
+ DIF_INSTALLDEVICE to install driver directly on the device, bypassing
the catalog signature check that blocks Methods 1 and 2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jim800121chen 2026-03-10 00:31:10 +08:00
parent 17110a1606
commit 0c45e77911
2 changed files with 215 additions and 42 deletions

View File

@ -12,6 +12,66 @@ import (
"unsafe"
)
var (
setupapi = syscall.MustLoadDLL("setupapi.dll")
newdev = syscall.MustLoadDLL("newdev.dll")
procSetupDiGetClassDevsW = setupapi.MustFindProc("SetupDiGetClassDevsW")
procSetupDiEnumDeviceInfo = setupapi.MustFindProc("SetupDiEnumDeviceInfo")
procSetupDiGetDeviceRegistryPropW = setupapi.MustFindProc("SetupDiGetDeviceRegistryPropertyW")
procSetupDiSetDeviceRegistryPropW = setupapi.MustFindProc("SetupDiSetDeviceRegistryPropertyW")
procSetupDiSetDevInstallParamsW = setupapi.MustFindProc("SetupDiSetDeviceInstallParamsW")
procSetupDiGetDevInstallParamsW = setupapi.MustFindProc("SetupDiGetDeviceInstallParamsW")
procSetupDiCallClassInstaller = setupapi.MustFindProc("SetupDiCallClassInstaller")
procSetupDiDestroyDevInfoList = setupapi.MustFindProc("SetupDiDestroyDeviceInfoList")
procSetupDiBuildDriverInfoList = setupapi.MustFindProc("SetupDiBuildDriverInfoList")
procUpdateDriverForPlugAndPlayDevicesW = newdev.MustFindProc("UpdateDriverForPlugAndPlayDevicesW")
procDiInstallDriverW = newdev.MustFindProc("DiInstallDriverW")
)
const (
DIGCF_PRESENT = 0x00000002
DIGCF_ALLCLASSES = 0x00000004
SPDRP_HARDWAREID = 0x00000001
DIF_SELECTBESTCOMPATDRV = 0x00000017
DIF_INSTALLDEVICE = 0x00000002
DI_FLAGSEX_ALLOWEXCLUDEDDRVS = 0x00000800
DI_ENUMSINGLEINF = 0x00010000
DI_DONOTCALLCONFIGMG = 0x00000002
INSTALLFLAG_FORCE = 0x00000001
DIIRFLAG_FORCE_INF = 0x00000002
SPDIT_COMPATDRIVER = 0x00000002
INVALID_HANDLE_VALUE = ^uintptr(0)
)
// SP_DEVINFO_DATA
type SP_DEVINFO_DATA struct {
cbSize uint32
ClassGuid [16]byte
DevInst uint32
Reserved uintptr
}
// SP_DEVINSTALL_PARAMS_W (size depends on arch: 584 bytes on amd64)
type SP_DEVINSTALL_PARAMS_W struct {
cbSize uint32
Flags uint32
FlagsEx uint32
hwndParent uintptr
InstallMsgHandler uintptr
InstallMsgHandlerContext uintptr
FileQueue uintptr
ClassInstallReserved uintptr
Reserved uint32
DriverPath [260]uint16
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: test_driver_install.exe <path-to-inf>")
@ -26,7 +86,6 @@ func main() {
os.Exit(1)
}
// Hardware ID for Kneron KL520
hardwareID := "USB\\VID_3231&PID_0100"
fmt.Println("")
@ -36,34 +95,23 @@ func main() {
fmt.Println("")
fmt.Println("=== Method 2: DiInstallDriverW ===")
tryDiInstallDriver(infPath)
fmt.Println("")
fmt.Println("=== Method 3: SetupAPI direct device node install ===")
trySetupAPIInstall(infPath, hardwareID)
}
func tryUpdateDriver(infPath, hardwareID string) {
newdev, err := syscall.LoadDLL("newdev.dll")
if err != nil {
fmt.Printf("ERROR: LoadDLL: %v\n", err)
return
}
defer newdev.Release()
proc, err := newdev.FindProc("UpdateDriverForPlugAndPlayDevicesW")
if err != nil {
fmt.Printf("ERROR: FindProc: %v\n", err)
return
}
infUTF16, _ := syscall.UTF16PtrFromString(infPath)
hwIDUTF16, _ := syscall.UTF16PtrFromString(hardwareID)
const INSTALLFLAG_FORCE = 0x00000001
var needReboot int32
fmt.Printf("Hardware ID: %s\n", hardwareID)
fmt.Println("Calling UpdateDriverForPlugAndPlayDevicesW...")
fmt.Println("(If a Windows Security dialog appears, click 'Install this driver software anyway')")
ret, _, lastErr := proc.Call(
0, // hwndParent = NULL
ret, _, lastErr := procUpdateDriverForPlugAndPlayDevicesW.Call(
0,
uintptr(unsafe.Pointer(hwIDUTF16)),
uintptr(unsafe.Pointer(infUTF16)),
INSTALLFLAG_FORCE,
@ -71,51 +119,176 @@ func tryUpdateDriver(infPath, hardwareID string) {
)
fmt.Printf("Return value: %d\n", ret)
fmt.Printf("NeedReboot: %d\n", needReboot)
if ret == 0 {
errno := lastErr.(syscall.Errno)
fmt.Printf("FAILED - error: %v (code: %d / 0x%08X)\n", lastErr, uint32(errno), uint32(errno))
} else {
fmt.Println("SUCCESS - driver installed!")
fmt.Println("SUCCESS!")
}
}
func tryDiInstallDriver(infPath string) {
newdev, err := syscall.LoadDLL("newdev.dll")
if err != nil {
fmt.Printf("ERROR: LoadDLL: %v\n", err)
return
}
defer newdev.Release()
proc, err := newdev.FindProc("DiInstallDriverW")
if err != nil {
fmt.Printf("ERROR: FindProc: %v\n", err)
return
}
infUTF16, _ := syscall.UTF16PtrFromString(infPath)
const DIIRFLAG_FORCE_INF = 0x00000002
var needReboot int32
fmt.Println("Calling DiInstallDriverW (DIIRFLAG_FORCE_INF)...")
fmt.Println("Calling DiInstallDriverW...")
ret, _, lastErr := proc.Call(
0, // hwndParent = NULL
ret, _, lastErr := procDiInstallDriverW.Call(
0,
uintptr(unsafe.Pointer(infUTF16)),
DIIRFLAG_FORCE_INF,
uintptr(unsafe.Pointer(&needReboot)),
)
fmt.Printf("Return value: %d\n", ret)
fmt.Printf("NeedReboot: %d\n", needReboot)
if ret == 0 {
errno := lastErr.(syscall.Errno)
fmt.Printf("FAILED - error: %v (code: %d / 0x%08X)\n", lastErr, uint32(errno), uint32(errno))
} else {
fmt.Println("SUCCESS - driver installed!")
fmt.Println("SUCCESS!")
}
}
func trySetupAPIInstall(infPath, targetHWID string) {
fmt.Println("Enumerating all present USB devices...")
// Get all present devices
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
0, // NULL = all classes
0, // no enumerator
0, // no hwnd
DIGCF_PRESENT|DIGCF_ALLCLASSES,
)
if hDevInfo == INVALID_HANDLE_VALUE {
fmt.Printf("ERROR: SetupDiGetClassDevsW failed: %v\n", err)
return
}
defer procSetupDiDestroyDevInfoList.Call(hDevInfo)
var devInfoData SP_DEVINFO_DATA
devInfoData.cbSize = uint32(unsafe.Sizeof(devInfoData))
found := false
for i := uint32(0); ; i++ {
ret, _, _ := procSetupDiEnumDeviceInfo.Call(
hDevInfo,
uintptr(i),
uintptr(unsafe.Pointer(&devInfoData)),
)
if ret == 0 {
break
}
// Get hardware ID
var buf [1024]uint16
var dataType, reqSize uint32
ret, _, _ = procSetupDiGetDeviceRegistryPropW.Call(
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
SPDRP_HARDWAREID,
uintptr(unsafe.Pointer(&dataType)),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(len(buf)*2),
uintptr(unsafe.Pointer(&reqSize)),
)
if ret == 0 {
continue
}
// MULTI_SZ: check each string
hwid := syscall.UTF16ToString(buf[:])
if hwid == targetHWID {
fmt.Printf("FOUND device: %s (DevInst: %d)\n", hwid, devInfoData.DevInst)
found = true
// Now try to install driver on this specific device
installOnDevice(hDevInfo, &devInfoData, infPath)
break
}
}
if !found {
fmt.Printf("Device with Hardware ID '%s' not found among present devices.\n", targetHWID)
}
}
func installOnDevice(hDevInfo uintptr, devInfoData *SP_DEVINFO_DATA, infPath string) {
fmt.Println("Setting up driver install params...")
// Get current install params
var installParams SP_DEVINSTALL_PARAMS_W
installParams.cbSize = uint32(unsafe.Sizeof(installParams))
ret, _, err := procSetupDiGetDevInstallParamsW.Call(
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
uintptr(unsafe.Pointer(&installParams)),
)
if ret == 0 {
fmt.Printf("ERROR: GetDeviceInstallParams failed: %v\n", err)
return
}
fmt.Printf("Current Flags: 0x%08X, FlagsEx: 0x%08X\n", installParams.Flags, installParams.FlagsEx)
// Set DriverPath to our INF and enable DI_ENUMSINGLEINF
infUTF16, _ := syscall.UTF16FromString(infPath)
copy(installParams.DriverPath[:], infUTF16)
installParams.Flags |= DI_ENUMSINGLEINF
installParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS
ret, _, err = procSetupDiSetDevInstallParamsW.Call(
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
uintptr(unsafe.Pointer(&installParams)),
)
if ret == 0 {
fmt.Printf("ERROR: SetDeviceInstallParams failed: %v\n", err)
return
}
fmt.Println("Install params set (DI_ENUMSINGLEINF, DriverPath set)")
// Build driver info list from our specific INF
fmt.Println("Building driver info list from INF...")
ret, _, err = procSetupDiBuildDriverInfoList.Call(
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
SPDIT_COMPATDRIVER,
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: BuildDriverInfoList failed: %v (code: 0x%08X)\n", err, uint32(errno))
return
}
fmt.Println("Driver info list built.")
// Select best compatible driver
fmt.Println("Selecting best compatible driver...")
ret, _, err = procSetupDiCallClassInstaller.Call(
DIF_SELECTBESTCOMPATDRV,
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: DIF_SELECTBESTCOMPATDRV failed: %v (code: 0x%08X)\n", err, uint32(errno))
fmt.Println(" (This might mean the INF is not compatible or catalog issue)")
return
}
fmt.Println("Best compatible driver selected.")
// Install the device
fmt.Println("Installing device with selected driver...")
fmt.Println("(If a Windows Security dialog appears, click 'Install this driver software anyway')")
ret, _, err = procSetupDiCallClassInstaller.Call(
DIF_INSTALLDEVICE,
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: DIF_INSTALLDEVICE failed: %v (code: 0x%08X)\n", err, uint32(errno))
} else {
fmt.Println("SUCCESS - driver installed on device!")
}
}