看到GPZ很久之前披露的一个在野cng.sys驱动漏洞,本菜鸟尝试对其进行简单分析,希望能从中学习到一些知识
漏洞概述:
内核驱动模块cng.sys
中存在整数溢出漏洞,利用此漏洞进行越界读写,最终可实现本地提取,此漏洞被黑客用于Chrome Sandbox Escape(CVE-2020-15999)
影响的windows版本:
Windows 10 2004以及之前的版本
Windows版本:win10 1903 10.0.18362.836
根据GPZ给出的Poc,可以定位到漏洞函数cng!CfgAdtpFormatPropertyBlock
,size
可以被用户控制,6 * size
的结果强行转换为无符号的16位整数,0x10000 // 0x6
的结果为0x2aa,0x10000 / 6
的结果为10922.666666666666,假设控制size的大小为0x2ab,将其乘上6就溢出为2,BCryptAlloc
函数内部会调用ExAllocatePoolWithTag或者SkAllocatePool在类型为NonPagedPoolNx池动态申请池空间,此时NumberOfBytes
为2,申请的空间就十分小,然而do-while循环的次数为size(即0x2ab),每次向池空间写入6字节,最终导致越界写入,触发BSOD
从驱动的cng!DriverEntry入口函数入手,发现好几个MajorFunction都设置为cng!CngDispatch
跟进cng!CngDispatch函数,可以看到switch中以Major Function Code作为条件,当前案例中关注的是case 0xe,宏IRP_MJ_DEVICE_CONTROL的值为0xe,用户态调用DeviceIoControl这个API最终执行到内核驱动模块中的DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]
IoCode即为IO控制码,从崩溃的栈帧回溯的执行路径获悉IoCode为0x390400,但是很遗憾,我的分析属于一种根据结果来推断过程(根据崩溃的栈帧),这也是四哥痛骂的那波人之一(我太菜了),代码审计的逻辑是从cng!CngDispatch函数开始,进入到cng!CngDeviceControl函数中根据不同IoCode进行递归,然后进行代码审计。本身来说这个漏洞成因相对简单,需要学习的是寻找触发漏洞路径
在cng!CngDispatch函数中下断点,然后查看一下cng!CngDeviceControl函数的6个参数,分别为输入缓冲区、输入缓冲区长度、输出缓冲区、输出缓冲区长度的指针、IO控制码、请求模式
从上图的第5个参数可以看到传入的IoCode为0x390400,因为抵达漏洞函数需要执行过函数cng!ConfigIoHandler_Safeguarded,cng!CngDeviceControl函数中存在这样的判断
cng!ConfigIoHandler_Safeguarded使用的参数为cng!CngDeviceControl前四个参数:rcx,rdx,r8,r9
跟进cng!ConfigIoHandler_Safeguarded函数,可以看到函数根据输入缓冲区的长度申请了两个大小相同的池块,姑且称为pool1、pool2,将输入缓冲区的内容拷贝至pool1,pool2初始化为0,在后续的分析过程,将pool1的首地址称为pool_ptr1,pool2的首地址称为pool_ptr2
在调试器中查看执行memmove前后的变化,验证以上说法
从伪代码中可以得知调用cng!IoUnpack_SG_ParamBlock_Header的返回值必须为0,否则函数直接返回,于是继续跟进cng!IoUnpack_SG_ParamBlock_Header,函数首先进行pool_ptr1 + 2 < pool_ptr1的判断,然后又进行pool_ptr1 + 2 > (_DWORD )((char )pool_ptr1 + size这样的判断,不太清楚这样做的意义,获取pool1的前4字节,与0x1A2B3C4D
进行比较,因此输入缓冲区的前4字节设置为0x1A2B3C4D,第3个参数所指内存会保存pool1的第二个4字节,数值为0x10400,for循环内会将pool2的前8字节置位
查看pool2在执行完循环前后的变化
回到cng!ConfigIoHandler_Safeguarded函数,函数继续调用cng!ConfigFunctionIoHandler函数,第一个参数传入的值为0x10400,HIWORD(a1)返回的结果为1,就会进入到cng!ConfigurationFunctionIoHandler函数,a2、a3、a4、a5、a6对应的是pool_ptr1、InputLen、OutputBuffer、&OutputLen、pool_ptr2
在调试器中查看cng!ConfigurationFunctionIoHandler函数的参数
cng!ConfigurationFunctionIoHandler函数开头调用cng!IoUnpack_SG_Configuration_ParamBlock,从函数名跟上文的分析来看,该函数会对池块的某些部分进行一些必要的检查并写入一些值,函数的返回值依然需要为0
跟进cng!IoUnpack_SG_Configuration_ParamBlock函数,先对pool1的大小与8进行对比,由于我们使用的大小为0x3aab,还不能直接返回0,将pool1的首地址加上0x68得到v18,当前实例中保证pool1 < v18 < (pool1 + poolsize),紧接着就是一连串的判断后决定是否调用cng!IoUnpack_SG_SzString、cng!IoUnpack_SG_ContextFunctionConfig、cng!IoUnpack_SG_Buffer函数,由于a4、a6、a7等都为变量的地址(不为0),故都会进入分支执行这些函数
在调试器中查看一下第一次执行完cng!IoUnpack_SG_SzString函数后的变化
在调试器中查看一下最后一次执行完cng!IoUnpack_SG_ContextFunctionConfig函数后的变化
在调试器中查看一下执行完cng!IoUnpack_SG_Buffer函数后的变化
从上面几张图片可以看到,执行完cng!IoUnpack_SG_SzString、cng!IoUnpack_SG_ContextFunctionConfig、cng!IoUnpack_SG_Buffer函数后,缓冲区偏移0x10、0x18、0x20、0x28、0x30、0x40、0x48、0x58的内容在原有的基础上加上了pool1的首地址
对cng!IoUnpack_SG_Buffer函数进行简单的分析,可以发现函数内向pool_ptr2偏移0x58字节写入8字节的0xff,在pool_ptr2偏移poi(pool_ptr1 + 0x58)(当前数值为0x1000)处开始写入poi(pool_ptr1 + 0x50)(当前数值为0x2ab)字节的0xff
在调试器中查看pool2的内容进行验证
执行完以上部分,紧接着就是一组赋值操作,然后返回0
返回到cng!ConfigurationFunctionIoHandler函数后,由于输入缓冲区的第二个四字节0x10400被截断为0x400,就会执行到这样的分支
跟进cng!BCryptSetContextFunctionProperty函数,继续分析此函数,cng!ValidateTableId所在的分支无法满足条件,利用cbValue和pbValue的值初始化DestinationString,利用此DestinationString调用cng!CfgReg_Acquire
进一步跟进cng!CfgReg_Acquire,发现cng!VerifyRegistryAccess函数尝试对System\CurrentControlSet\Control\Cryptography\Configuration\Local注册表项执行操作,cng!VerifyRegistryAccess内部调用cng!KeRegOpenKey尝试打开注册表表项
根据cng!VerifyRegistryAccess的返回值为5得知打开失败,函数将5返回给cng!BCryptSetContextFunctionProperty
紧接着在cng!BCryptSetContextFunctionProperty的执行流程大体如下,分别使用pool1_ptr+0x100、pool1_ptr+0x200、pool1_ptr+0x400处的字符初始化三个新的UnicodeString
在初始化三个UnicodeString后,进入下面的分支调用cng!CfgAdtReportFunctionPropertyModification函数
在调试器中查看一下函数的参数,最少有10个,IDA反编译出来的结果中有11个
从上图中可以看到,在rsp+40的位置存储了0x2aab,这个数值来自于pool_ptr1 + 0x50的位置,查看一下寄存器r14w的数据来源,可以看到函数序言部分将pool_ptr1 + 0x50处的值赋给r14d
cng!CfgAdtReportFunctionPropertyModification函数内部调用最终的漏洞函数cng!CfgAdtpFormatPropertyBlock,调试器中查看一下cng!CfgAdtpFormatPropertyBlock的参数,可以看到第二个参数来自于cng!CfgAdtReportFunctionPropertyModification的第九个参数,数值为0x2aab
由于漏洞原理在开篇提及,此处就对一些关键位置查看下。查看一下cng!BCryptAlloc的参数,果然溢出为2
调用完该函数申请到的池块大小为0x20字节
然而循环的次数为0x2aab次,每次向池块中写入6字节,最终就会覆写到相邻的内存,最终导致BSOD
对比一下补丁前后的文件变化,可以看到只有cng!CfgAdtpFormatPropertyBlock函数发生了变化
查看一下函数的前后变化,发现补丁后函数调用cng!RtlUShortMult函数对传进来的size进行了处理,然后再传给cng!BCryptAlloc函数申请池空间
cng!RtlUShortMult函数十分简短,就是对size * 6的结果进行溢出判断(与0xffff比较)
__int64 __fastcall CfgAdtpFormatPropertyBlock(char
*
a1, unsigned __int16 size, __int64 a3)
{
unsigned
int
v3;
/
/
ebx
char
*
v6;
/
/
r14
__int16 v7;
/
/
di
_WORD
*
pool_ptr;
/
/
rax
_WORD
*
v9;
/
/
rdx
_WORD
*
pool_ptr_w;
/
/
rcx
__int64 count;
/
/
r8
_WORD
*
v12;
/
/
rcx
char v13;
/
/
al
v3
=
0
;
v6
=
a1;
if
( a1 && size && a3 )
{
v7
=
6
*
size;
pool_ptr
=
BCryptAlloc((unsigned __int16)(
6
*
size));
/
/
6
*
0x2aab
=
2
v9
=
pool_ptr;
if
( pool_ptr )
{
pool_ptr_w
=
pool_ptr;
if
( size )
{
count
=
size;
do
{
/
/
store
6
bytes every time
*
pool_ptr_w
=
(unsigned __int8)a0123456789abcd[(unsigned __int64)(unsigned __int8)
*
v6 >>
4
];
v12
=
pool_ptr_w
+
1
;
v13
=
*
v6
+
+
;
*
v12
+
+
=
(unsigned __int8)a0123456789abcd[v13 &
0xF
];
*
v12
=
0x20
;
pool_ptr_w
=
v12
+
1
;
-
-
count;
}
while
( count );
}
*
(_QWORD
*
)(a3
+
8
)
=
v9;
*
(_WORD
*
)(a3
+
2
)
=
v7;
*
(_WORD
*
)a3
=
v7
-
2
;
}
else
{
return
0xC000009A
;
}
}
else
{
return
0xC000000D
;
}
return
v3;
}
PVOID __fastcall BCryptAlloc(SIZE_T NumberOfBytes)
{
char DeviceContext;
/
/
al
DeviceContext
=
(char)WPP_MAIN_CB.Queue.Wcb.DeviceContext;
if
( !LODWORD(WPP_MAIN_CB.Queue.Wcb.DeviceContext) )
DeviceContext
=
GetTrustedEnvironment();
if
( (DeviceContext &
2
) !
=
0
)
return
(PVOID)SkAllocatePool(
0x200i64
, NumberOfBytes,
'bgnC'
);
/
/
NonPagedPoolNx
else
return
ExAllocatePoolWithTag((POOL_TYPE)
0x200
, NumberOfBytes,
'bgnC'
);
/
/
NonPagedPoolNx
}
__int64 __fastcall CfgAdtpFormatPropertyBlock(char
*
a1, unsigned __int16 size, __int64 a3)
{
unsigned
int
v3;
/
/
ebx
char
*
v6;
/
/
r14
__int16 v7;
/
/
di
_WORD
*
pool_ptr;
/
/
rax
_WORD
*
v9;
/
/
rdx
_WORD
*
pool_ptr_w;
/
/
rcx
__int64 count;
/
/
r8
_WORD
*
v12;
/
/
rcx
char v13;
/
/
al
v3
=
0
;
v6
=
a1;
if
( a1 && size && a3 )
{
v7
=
6
*
size;
pool_ptr
=
BCryptAlloc((unsigned __int16)(
6
*
size));
/
/
6
*
0x2aab
=
2
v9
=
pool_ptr;
if
( pool_ptr )
{
pool_ptr_w
=
pool_ptr;
if
( size )
{
count
=
size;
do
{
/
/
store
6
bytes every time
*
pool_ptr_w
=
(unsigned __int8)a0123456789abcd[(unsigned __int64)(unsigned __int8)
*
v6 >>
4
];
v12
=
pool_ptr_w
+
1
;
v13
=
*
v6
+
+
;
*
v12
+
+
=
(unsigned __int8)a0123456789abcd[v13 &
0xF
];
*
v12
=
0x20
;
pool_ptr_w
=
v12
+
1
;
-
-
count;
}
while
( count );
}
*
(_QWORD
*
)(a3
+
8
)
=
v9;
*
(_WORD
*
)(a3
+
2
)
=
v7;
*
(_WORD
*
)a3
=
v7
-
2
;
}
else
{
return
0xC000009A
;
}
}
else
{
return
0xC000000D
;
}
return
v3;
}
PVOID __fastcall BCryptAlloc(SIZE_T NumberOfBytes)
{
char DeviceContext;
/
/
al
DeviceContext
=
(char)WPP_MAIN_CB.Queue.Wcb.DeviceContext;
if
( !LODWORD(WPP_MAIN_CB.Queue.Wcb.DeviceContext) )
DeviceContext
=
GetTrustedEnvironment();
if
( (DeviceContext &
2
) !
=
0
)
return
(PVOID)SkAllocatePool(
0x200i64
, NumberOfBytes,
'bgnC'
);
/
/
NonPagedPoolNx
else
return
ExAllocatePoolWithTag((POOL_TYPE)
0x200
, NumberOfBytes,
'bgnC'
);
/
/
NonPagedPoolNx
}
DriverObject
-
>MajorFunction[
0
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
2
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
3
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
4
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
5
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
0xA
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
0xE
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
0
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
2
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
3
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
4
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
5
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
0xA
]
=
(PDRIVER_DISPATCH)CngDispatch;
DriverObject
-
>MajorFunction[
0xE
]
=
(PDRIVER_DISPATCH)CngDispatch;
switch ( CurrentStackLocation
-
>MajorFunction )
{
...
case
0xEu
:
IoCode
=
CurrentStackLocation
-
>Parameters.Read.ByteOffset.LowPart;
if
( (IoCode &
3
)
=
=
2
&& CurrentStackLocation
-
>Parameters.Read.Length )
{
...
}
else
{
OutputBuffer
=
irp
-
>AssociatedIrp.MasterIrp;
InputBuffer
=
OutputBuffer;
OutputLen
=
CurrentStackLocation
-
>Parameters.Read.Length;
}
irp
-
>IoStatus.Status
=
CngDeviceControl(
InputBuffer,
CurrentStackLocation
-
>Parameters.Create.Options,
/
/
InputLen
OutputBuffer,
&OutputLen,
IoCode,
irp
-
>RequestorMode);
irp
-
>IoStatus.Information
=
OutputLen;
break
;
}
switch ( CurrentStackLocation
-
>MajorFunction )
{
...
case
0xEu
:
IoCode
=
CurrentStackLocation
-
>Parameters.Read.ByteOffset.LowPart;
if
( (IoCode &
3
)
=
=
2
&& CurrentStackLocation
-
>Parameters.Read.Length )
{
...
}
else
{
OutputBuffer
=
irp
-
>AssociatedIrp.MasterIrp;
InputBuffer
=
OutputBuffer;
OutputLen
=
CurrentStackLocation
-
>Parameters.Read.Length;
}
irp
-
>IoStatus.Status
=
CngDeviceControl(
InputBuffer,
CurrentStackLocation
-
>Parameters.Create.Options,
/
/
InputLen
OutputBuffer,
&OutputLen,
IoCode,
irp
-
>RequestorMode);
irp
-
>IoStatus.Information
=
OutputLen;
break
;
}
1
: kd> ba e1
/
w
"@$curprocess.Name == \"poc.exe\""
cng!CngDispatch
+
85
1
: kd> ba e1
/
w
"@$curprocess.Name == \"poc.exe\""
cng!CngDispatch
+
85
if
( IoCode
=
=
0x390400
)
return
ConfigIoHandler_Safeguarded(InputBuffer, NumberOfBytes, (IRP
*
)OutputBuffer, OutputLen);
if
( IoCode
=
=
0x390400
)
return
ConfigIoHandler_Safeguarded(InputBuffer, NumberOfBytes, (IRP
*
)OutputBuffer, OutputLen);
__int64 __fastcall ConfigIoHandler_Safeguarded(
PVOID InputBuffer,
SIZE_T InputLen,
PVOID OutputBuffer,
ULONG
*
OutputLen)
{
...
size
=
InputLen;
if
( OutputBuffer )
v7
=
*
OutputLen;
/
/
v7
=
8
else
v7
=
0
;
*
OutputLen
=
0
;
v8
=
8
;
len
=
v7;
/
/
len
=
8
if
( v7 <
8
)
{
if
( v7 )
memset(OutputBuffer,
0
, v7);
return
0xC0000023
;
}
else
{
v9
=
(unsigned
int
)InputLen;
pool_ptr1
=
BCryptAlloc((unsigned
int
)InputLen);
/
/
allocate pool1
v11
=
BCryptAlloc(v9);
/
/
allocate pool2
pool_ptr2
=
v11;
if
( pool_ptr1 && v11 )
{
memmove(pool_ptr1, InputBuffer, v9);
/
/
copy InputBuffer to pool1
memset(pool_ptr2,
0
, v9);
/
/
clear pool2
v13
=
IoUnpack_SG_ParamBlock_Header(pool_ptr1, size, &v19, pool_ptr2);
if
( v13 )
{
v15
=
WinErrorToNtStatus(v13);
}
else
{
v14
=
ConfigFunctionIoHandler(
v19,
(
int
)pool_ptr1,
size,
(struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS
*
)OutputBuffer,
&
len
,
(__int64)pool_ptr2);
...
}
...
}
...
}
__int64 __fastcall ConfigIoHandler_Safeguarded(
PVOID InputBuffer,
SIZE_T InputLen,
PVOID OutputBuffer,
ULONG
*
OutputLen)
{
...
size
=
InputLen;
if
( OutputBuffer )
v7
=
*
OutputLen;
/
/
v7
=
8
else
v7
=
0
;
*
OutputLen
=
0
;
v8
=
8
;
len
=
v7;
/
/
len
=
8
if
( v7 <
8
)
{
if
( v7 )
memset(OutputBuffer,
0
, v7);
return
0xC0000023
;
}
else
{
v9
=
(unsigned
int
)InputLen;
pool_ptr1
=
BCryptAlloc((unsigned
int
)InputLen);
/
/
allocate pool1
v11
=
BCryptAlloc(v9);
/
/
allocate pool2
pool_ptr2
=
v11;
if
( pool_ptr1 && v11 )
{
memmove(pool_ptr1, InputBuffer, v9);
/
/
copy InputBuffer to pool1
memset(pool_ptr2,
0
, v9);
/
/
clear pool2
v13
=
IoUnpack_SG_ParamBlock_Header(pool_ptr1, size, &v19, pool_ptr2);
if
( v13 )
{
v15
=
WinErrorToNtStatus(v13);
}
else
{
v14
=
ConfigFunctionIoHandler(
v19,
(
int
)pool_ptr1,
size,
(struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS
*
)OutputBuffer,
&
len
,
(__int64)pool_ptr2);
...
}
...
}
...
}
__int64 __fastcall IoUnpack_SG_ParamBlock_Header(_DWORD
*
pool_ptr1, unsigned
int
size, _DWORD
*
a3, _QWORD
*
pool_ptr2)
{
...
v4
=
0
;
if
( !pool_ptr1 )
return
1i64
;
if
( pool_ptr1
+
2
< pool_ptr1 )
return
1i64
;
v5
=
size;
if
( pool_ptr1
+
2
> (_DWORD
*
)((char
*
)pool_ptr1
+
size) ||
*
pool_ptr1 !
=
0x1A2B3C4D
)
return
1i64
;
if
( a3 )
*
a3
=
pool_ptr1[
1
];
if
( !pool_ptr2 )
return
0i64
;
v6
=
pool_ptr2
+
1
;
if
( pool_ptr2
+
1
>
=
pool_ptr2 )
{
if
( v6 <
=
(_QWORD
*
)((char
*
)pool_ptr2
+
size) )
{
v7
=
0
;
for
( i
=
pool_ptr2; !
*
i;
+
+
i )
{
if
( (unsigned
int
)
+
+
v7 >
=
8
)
{
*
pool_ptr2
=
0xFFFFFFFFFFFFFFFFui64
;
return
0i64
;
}
...
}
...
}
...
}
...
}
__int64 __fastcall IoUnpack_SG_ParamBlock_Header(_DWORD
*
pool_ptr1, unsigned
int
size, _DWORD
*
a3, _QWORD
*
pool_ptr2)
{
...
v4
=
0
;
if
( !pool_ptr1 )
return
1i64
;
if
( pool_ptr1
+
2
< pool_ptr1 )
return
1i64
;
v5
=
size;
if
( pool_ptr1
+
2
> (_DWORD
*
)((char
*
)pool_ptr1
+
size) ||
*
pool_ptr1 !
=
0x1A2B3C4D
)
return
1i64
;
if
( a3 )
*
a3
=
pool_ptr1[
1
];
if
( !pool_ptr2 )
return
0i64
;
v6
=
pool_ptr2
+
1
;
if
( pool_ptr2
+
1
>
=
pool_ptr2 )
{
if
( v6 <
=
(_QWORD
*
)((char
*
)pool_ptr2
+
size) )
{
v7
=
0
;
for
( i
=
pool_ptr2; !
*
i;
+
+
i )
{
if
( (unsigned
int
)
+
+
v7 >
=
8
)
{
*
pool_ptr2
=
0xFFFFFFFFFFFFFFFFui64
;
return
0i64
;
}
...
}
...
}
...
}
...
}
1
: kd> dq @rax l4
ffffbb0f`
7e160000
00000000
`
00000000
00000000
`
00000000
ffffbb0f`
7e160010
00000000
`
00000000
00000000
`
00000000
1
: kd> dq @r9 l4
ffffbb0f`
7e160000
ffffffff`ffffffff
00000000
`
00000000
ffffbb0f`
7e160010
00000000
`
00000000
00000000
`
00000000
1
: kd> dq @rax l4
ffffbb0f`
7e160000
00000000
`
00000000
00000000
`
00000000
ffffbb0f`
7e160010
00000000
`
00000000
00000000
`
00000000
1
: kd> dq @r9 l4
ffffbb0f`
7e160000
ffffffff`ffffffff
00000000
`
00000000
ffffbb0f`
7e160010
00000000
`
00000000
00000000
`
00000000
NTSTATUS __fastcall ConfigFunctionIoHandler(
unsigned
int
a1,
int
a2,
unsigned
int
a3,
struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS
*
a4,
_DWORD
*
a5,
__int64 a6)
{
switch ( HIWORD(a1) )
{
case
0u
:
return
RegistrationFunctionIoHandler(a1, a2, a3, (
int
)a4, (ULONG)a5, a6);
case
1u
:
return
ConfigurationFunctionIoHandler(a1, a2, a3, a4, a5, a6);
case
2u
:
return
ResolutionFunctionIoHandler(a1, a2, a3, (
int
)a4, (ULONG)a5, a6);
}
return
0xC00000AF
;
}
NTSTATUS __fastcall ConfigFunctionIoHandler(
unsigned
int
a1,
int
a2,
unsigned
int
a3,
struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS
*
a4,
_DWORD
*
a5,
__int64 a6)
{
switch ( HIWORD(a1) )
{
case
0u
:
return
RegistrationFunctionIoHandler(a1, a2, a3, (
int
)a4, (ULONG)a5, a6);
case
1u
:
return
ConfigurationFunctionIoHandler(a1, a2, a3, a4, a5, a6);
case
2u
:
return
ResolutionFunctionIoHandler(a1, a2, a3, (
int
)a4, (ULONG)a5, a6);
}
return
0xC00000AF
;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2022-3-16 07:53
被Jimpp编辑
,原因:
上传的附件: