-
-
[原创]CVE-2012-0003 winmm.dll MIDI文件堆溢出漏洞分析及利用
-
发表于: 2021-9-13 18:30 18614
-
这次分析了CVE-2012-0003 MIDI文件中存在的堆溢出漏洞,此次分析结合了《漏洞战争》以及VUPEN发布的关于该漏洞的介绍中的内容,从原理及利用两个角度对该漏洞进行了分析。
文章一开始对MIDI文件的结构进行了简单的介绍,之后根据IDA得到的伪代码,利用书中介绍的导图推算的方法确定了漏洞产生的原因。最后的漏洞利用部分花费了一些功夫,因为VUPEN中缺少具体细节,使用的利用代码并没有成功进行漏洞利用,书中也没有提到相关问题,所以文章中也涉及到我是如何对代码进行的修改,并分析了产生问题的原因。
MIDI文件是由块(chunk)组成的,每个块开头有4字节type信息及4字节length信息,length信息中表示的是该块之后的数据长度,即整个块的长度为length+8字节。
块分为两个类型:header chunk和track chunk,其基本组成如下图所示:
其中Delta-time+event表示一个MTrk event,它的个数不定,长度不定,所以画成了图中这样参差不齐的形式。
下面一个小节会根据具体文件来分析MIDI文件组成,这里先给出会涉及到的其他概念:
这种类型的数据信息用每个字节中的低七个比特表示,最高位为先。所有字节中,除最后一个字节外,其余字节的最高比特位置1,最后一个字节的最高比特位置0。由此确定该可变长度数据的终止位置。
在track trunk中,event又分为MIDI event、sysex event和meta-event。其中meta-event的结构如下:
它以0xFF
开头,接下来是一个字节的type信息,之后的length信息是variable length quantity,然后根据length信息填入对应长度的bytes信息。
几个涉及到的type信息:
MIDI event(MIDI message)通常由1字节的状态码以及1~2个字节的数据信息组成:
在下面的实例分析中,主要涉及到的是Channel Message,涉及到的状态码为0x99
、0x9F
、0xB9
和0xC9
,其中的9
和F
表示是第九个channel。这里对这两个状态码做一下解释:
接下来根据书中提供的test_case.mid文件,实例讲解MIDI文件构成。test_case.mid内容如下:
按照2.1中给出的结构分解:
对于MIDI文件格式的理解就到这里,并没有覆盖的所有的信息,更多内容可以看参考资料1。除此之外,很多专业名词的解释我也不太清楚,但是对于漏洞分析本身没什么影响。
环境:Win XP SP3
IE:6.0.2900
接下来正式开始对漏洞进行分析调试,由于这是一个堆溢出漏洞,所以在调试之前先为IE开启页堆。
打开cve-2012-0003-ie6.htm文件,使用windbg附加,F5继续执行,然后在IE上允许脚本运行,程序中断:
看一下堆栈信息:
可以看到这个堆块的起始地址为0x2797ec00
,大小为0x400
,0x2797f019
已经超过了这个堆块允许的最大地址范围0x2797f000
,所以触发了异常。
再看一下函数调用情况:
所以目前想要确定的应该是0x2797f019
这个地址是怎么得到的。找到WINMM.dll这个文件并在IDA中打开,找到midiOutPlayNextPolyEvent
这个函数。
异常发生位置处的代码为:
其中的v19和v16的来源:
其中wParam
就是该函数的参数,但是根据函数调用情况中得到的参数b2ceeb54
查看内存内容,发现全是问号,可能是在执行过程中有修改:
除此之外,如果观察midiOutPlayNextPolyEvent
函数的代码,会发现它一直在基于wParam
的某个偏移取值,所以现在关键是确定wParam
是什么。
重新调试(我之前建立了快照),这次在midiOutPlayNextPolyEvent
函数这里设置一个断点:
实验的时候发现第一次中断并不是发生异常的那次调用,第二次才是。看一下esp的内容:
此时还没有进行栈帧的分配,所以wParam
的值为27db4f60
,看一下这里的内容:
老实讲看不出什么来.所以还是要继续往下单步调试,查看一下数据来源。
但是调试之前,要先对IDA中的代码进行一些简单的分析。
在《漏洞战争》中提到了”导图推算“的分析方法,由于在midiOutPlayNextPolyEvent
函数中可以很清晰的看到它在一直在基于wParam
的某个偏移取值,并进行一些比较判断和数值运算,而最终我们关注的异常发生位置的变量取值也是在此基础上不断进行数值传递得到的。因此可以根据IDA中得到的代码,整理出该取值的数值传递流程,然后在调试器中设置多个断点,将各相关变量的取值变化打印出来。
如果不参考书中的方法,我应该也能想到去查看伪代码,但是可能不会像书中一样完整的整理出来,而是会选择在逐步调试的过程中进行确定。
对IDA生成的伪代码进行精简,专注于最终异常发生位置代码v21 = *v20;
中变量的传递流程,得到:
画出对应的流程图:
再整理出这几个关键变量的赋值位置:
但是有一个问题需要注意,部分指令不能直接在上述位置设置断点,而是应该在下一句指令处设置断点,获得赋值后的数值。
继续做整理:
还是让程序中断在midiOutPlayNextPolyEvent
函数的起始位置,设置好断点之后继续执行,每次都会中断在函数开头:
原始输出比较乱,整理成表格之后得到:
首先吸引我注意力的就是V17
,很明显这里保存的是状态码,而WParam_3a
中保存的是状态码的下一个字节数据。所以说这个函数在循环处理文件中的不同event的信息,在处理到最后一个event,即00 9F B2 73 // 0xB2号音符开始
的时候,出现了异常。
除此之外,可以发现:
在第一次函数调用时,函数并不会执行到处理v19
和v20
的代码处。
从IDA的伪代码也可以看出来,在处理这两个变量之前,程序会进行一次判断:
也就是说,异常发生在函数处理8x
或者9x
的状态码的时候。
再次回顾,发生异常的数值V20
的计算公式是:
根据上面的分析,V16
是一个基地址,而偏移地址V19
的计算公式是:
其中wParam_3a
是音符编号,最高比特位是0
,V17
是状态码,一定以9
开头。
要想实现堆溢出,一定要让V19
的值尽可能的大。按照MIDI的格式规范,wParam_3a
包含7比特有效数据,最大值为0x7F
,但是这里为了实现溢出,选择了0xB2
。而为了通过判断条件,V17
可变数据只占用四个比特,最大值为0x9F
。这里就选择了这个最大值。
再具体看一下0xB2
这个值的选取,基地址V16
的值是27db6c00
,该地址信息:
所以这是一个大小为0x400
的堆块,所以偏移只有超过0x400
才会发生堆溢出。
当V17
取最大值0x9F
的时候,((v17 & 0xF) << 7)
计算结果为0x780
,因此wParam_3a
的值至少要到0x80
才会发生堆溢出。也就是说对于合法的MIDI文件来说(即最高比特位为0),此处代码不会发生堆溢出。
可以实验一下,把测试用的文件中0xB2
修改成0x80
,重新打开cve-2012-0003-ie6.htm,同样发生了异常:
之后把这个值修改成0x7F
,没有发生异常。
还有一个小问题,我们在调试器里查看这个堆块的大小是0x400
,但是这个大小是怎么得到的呢?是硬编码的大小吗?
从上面的输出信息中可以看出这个堆块是在WINMM!mseOpen
函数中调用winmmAlloc
函数生成的,我们在IDA中找到这个函数,可以看到:
堆块大小确实是硬编码到程序中的,所以一旦偏移超过,就会发生堆溢出。
在分析怎样利用该漏洞时,先确定一下这个漏洞能够做些什么,重新看一下IDA中,异常发生位置之后的代码:
注意到代码*v20 = v23;
,这句代码会对溢出位置执行写入操作,所以一定要执行到这里,也就是说:
其中的v21
和v22
都是读取溢出位置得到的数值。
综上,wParam_3a
要是偶数,且溢出位置数值低四位不能全是1,如果满足以上条件,漏洞可以对溢出位置实现增1操作。
wParam_3a
不是偶数的条件很好做到,需要注意的就是溢出位置数值低四位的问题。
可以使用IE加载存在漏洞的MIDI文件,而IE的mshtml.dll和MIDI的winmm.dll中,用于分配堆块的堆空间都来自于GetProcessHeap
函数。两者使用同一个堆空间,因此有机会在IE中实现该漏洞的利用。
进一步的解释,在mshtml.dll的_DllMainStartup
函数(入口点函数)中:
该函数的定义:
Retrieves a handle to the default heap of the calling process
而winmm.dll中的__DllInstanceInit
函数中调用了_DllProcessAttach
:
转成伪代码:
因此当IE加载MIDI文件的时候,两者使用的是同一个堆空间,即进程的默认堆。
现在已知的是产生漏洞的堆空间大小为0x400,但由于漏洞的存在,导致程序可以对超出该堆空间的内存位置进行增1的操作。如果想要进行漏洞利用,首先应该能够做到分配一个同样大小的堆空间。根据参考资料2,在进行元素的复制的时候,有可能会分配到合适的空间大小。而cve-2012-0003-ie6.html这个文件中的漏洞代码中可以看到:
代码创建了一个包含64个属性的select
元素selob
,然后创建了一个1000大小的数组clones
,并将selob
循环赋值到数组clones
中,再对偶数位置上的空间进行了释放。这样的一系列操作可以形成空闲块、占用块相互间隔的堆空间结构。但是具体的细节还不清晰。
如果在Windbg中搜索和数组复制相关的函数,可以得到:
如果在该函数上设置断点,程序也会多次中断在这里,说明代码在赋值selob
元素的时候,确实调用了这个函数。在IDA中看一下这个函数的代码:
可以看到一个_MemAlloc
的函数调用,但是这里只分配了0xC
的空间,在往下看,有一个CImplAry::EnsureSize
的调用,这个函数也很有意思,里面会进行一个空间分配:
在Windbg中看一下调用到这个函数时的情况:
也就是说这个函数会分配0x42*0x10
大小的空间。我看到这个数值的时候就觉得可能会和代码中属性的个数有关,参考资料2中也说EnsureSize
会根据属性的个数来分配空间,但是资料中说如果属性有64个的话,那应该分配的就是0x40*0x10
的空间,但是我这里看到的却是0x42
。目前还不清楚是资料中有一些细节没有提到,还是环境导致的,先继续往下看。
如果按照资料中所说,最后分配了0x400
的空间,那么在进行了偶数位置空间的释放之后,每个空闲的0x400
大小的堆块后面都会有一个0x400
大小的由我们控制的堆块空间,这样一来,漏洞代码申请的0x400
大小的空间后面就是我们控制的数据了,接下来需要做的是让增1操作发生的时候,能够引发虚函数指针的泄露,从而控制程序的执行流程。
先来观察一下这个我们控制的0x400
大小的堆块数据中是什么内容,因为EnsureSize
调用之后,会使用CAttrValue::Copy
函数对数据进行复制,在Copy这个函数中,数据的复制操作是这样实现的:
可以看到数据从第一个参数的位置复制到了ecx
所指向的位置,所以可以直接检查调用Copy
之前,第一个参数所指向的位置:
根据参考资料2,3942ba0
处的数据,第二列表示的就是属性的不同类型,其中0x09
表示表示object
,0x08
表示string
,这也和selob
中对于属性的设置相符:
注意到object类型和string类型代表的数值之间相差了1,如果利用漏洞造成的增1操作,就有可能把原本的string类型修改为object类型,从而导致原本的字符串内容当作虚表指针被引用,这也是这次漏洞利用的原理。
再检查一下目标地址的情况:
可以看到确实是在EnsureSize
中分配的空间,但是大小有0x480
,和一开始期待的0x400
并不相同。
在3.3小节的最后,我们已经确定了发生异常的0x400
空间的分配位置是位于76B5CDE9
的_winmmAlloc
函数调用。而在本小节,也确定了漏洞利用时的分配空间操作是在EnsureSize
函数中完成的,可以根据函数调用之后ecx的值确认分配的空间位置,这里选择位于.text:7DDAE80D 8B 48 04 mov ecx, [eax+4]
之后的操作。
在这两个位置下断点,我们可以确定发生异常时的空间分配是否真的占用了漏洞利用代码中释放的空间:
如果这时候直接执行查看输出结果,会发现winmmAlloc
得到的空间地址和Clone
函数中得到的空间地址没有相同的,这是因为开启了页表,需要关闭页表,然后在原本发生异常的位置76b5d224
设置一个断点,然后重新调试。
截取部分输出内容:
最后中断在原本异常发生位置:
第一次中断位置在0020ca91
,这个值距离分配的空间起点为0x251
,和3.2小节中得到的v19
的第一次偏移量相同;第二次中断在0020cc59
,距离分配的空间起点为419
,确实比空间大小0x400
多了0x19
个字节。
唯一的问题就在于,不知道是什么原因,EnsureSize
函数分配的空间大小不是0x400
,而是0x480
现在先不去管为什么分配的空间多了,我直接修改了测试文件,删除了最后的两个属性w62
和w63
,重新调试,断点不变,得到的输出结果截取:
可以看到这次中断在了001e86a9
位置,距离winmmAlloc
分配的空间0x419
,而且也和Clone分配的空间001e8698
距离为0x11
(因为每个堆块还是8字节的头部),但是读取的字节是0x09
,而不是我们预期的0x08
。观察一下001e8698
空间:
第一行的内容貌似并不是w0
object属性,还是绕过原因不谈,再次删掉w0
属性,同时恢复一个之前删除的属性w62
,再次调试,保持断点不变:
这次读取到的字节是0x08
,如果继续往下单步调试,会发现这里的数值确实修改成了0x09
。
根据参考资料2,被修改成object
的string
元素之后会用来触发CAttrValue::GetIntoVariant()
函数的调用,也就是漏洞利用代码中的clones[k].w1('come on!');
语句,注意这是一个函数调用。直接在IDA中查看一下这个函数的代码:
可以看到,如果类型是object的话,最后会有一个函数调用:(*(void (__stdcall **)(_DWORD))(*(_DWORD *)v4 + 4))(*((_DWORD *)this + 3));
我们直接在这个函数调用之前设置一个断点,然后继续执行,中断在这个位置:
注意这里从eax指向的位置取出的值,就是漏洞利用代码中selob.w1 = unescape("%u1be4%u0c0c")
中的值,继续单步到达函数调用处:
跳转的地址正是0x0C0C0C0C
。
因为我不确定原本的漏洞利用代码中shellcode的作用,执行之后也没看到效果,所以我把原本的shellcode换成了弹计算器的代码,最后成功弹出了计算器:
通过对原始漏洞利用代码的调试,以及参考资料2中的内容,确定了漏洞利用的原理,并且对原始代码进行修改,成功进行了漏洞利用。现在唯一的问题就在于,为什么要进行这样的修改。
对于漏洞利用代码的修改主要集中在select元素selob
的属性个数以及string属性的位置上。按照参考资料2的说法,EnsureSize
函数分配的空间大小应该是0x10*属性个数
,但是在我实验的过程中,发现设置64个属性后分配的空间大小为0x480
。我们来详细看一下EnsureSize
函数,最后分配空间的代码是v13 = _MemAlloc(dwBytes);
,追踪一下dwBytes
这个变量:
不知道出于什么原因,64个属性值最后传入EnsureSize
函数的数值是0x42
,再加上对齐操作,导致0x42
变成了0x48
,结果分配了0x480
空间。同样,由于有这个对齐操作,即使设置的不是64个属性,最终也能分配到0x400
的空间。
还有一个问题就是在查看clones
数组中每一个复制之后得到的数据时,发现第一行数据并不是selob
的第一个属性内容,因此我在修改漏洞利用代码时,删除了属性w0
,让string属性w1
成为第一个属性(或者也可以修改MIDI文件)。
回到程序刚刚调用CAttrArray::Clone
函数的快照,取消页表,并删除w62
和w63
属性,如果在该函数中的EnsureSize
调用之后以及Copy
调用之后设置断点,在第一次中断之后可以确定程序分配的空间起始地址:
起始地址为ecx寄存器的内容020c0450
,之后继续执行,断在第二个断点,此时应该完成了第一个属性的复制,查看一下之前分配的空间内容:
发现第一行内容就是第一个属性的内容,那么为什么在之前调试时会出现问题呢?在020c0450
这里设置一个写断点:
程序断在了CImplAry::InsertIndirect
函数内,看一下函数调用流程:
也就是说,在Clone
函数中,复制完属性信息之后,程序又调用了EnsureHeader
函数,并在所有属性信息之前,直接插入了10字节的头部信息,所有属性信息后移一位(10个字节)。
至少现在我知道传入EnsureSize
函数中的数值大于0x40的原因了,因为它还要为头部预留空间,可能还有一些其他信息也要预留空间,因此最终预留了0x20
的空间。
在参考资料2中也提到了,这个漏洞除了可以实现增1的操作外,也可以实现减1的操作。在发生异常的midiOutPlayNextPolyEvent
函数中:
可以看到如果状态码是8x的形式,程序流程就会进入到上面的判断语句,然后执行到减1的操作。
这次漏洞分析中,只要了解了MIDI文件格式,那么一旦确定异常发生位置,在IDA中找到对应函数及其伪代码,通过对伪代码的分析,很容易确定漏洞产生的原理,其中书中介绍的导图推算的方法,画出变量的数值传递流程图,会让整个过程更加清晰。
VUPEN采用的漏洞利用方法十分新颖,具有借鉴意义。利用CImplAry中每个属性占据0x10空间的原理,不断分配和释放同样大小的堆空间,从而控制漏洞函数所分配空间周围的空间数据;利用string属性和object属性之间差值为1的特性,使用该漏洞将string属性变成了object属性,从而暴露出虚函数地址,让程序流程跳转到攻击者控制的数据中。
由于漏洞利用代码本身存在错误(和msf生成的不同),在修正问题的过程中,虽然花费了更多的时间,但是对漏洞利用的手法也了解的更为深刻。
Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004)
注:该网址已失效,可以在archive.org中搜索
4D
54
68
64
00
00
00
06
00
00
00
01
00
60
4D
54
72
6B
00
00
00
35
00
FF
03
0D
44
72
75
6D
73
20
20
20
28
42
42
29
00
00
C9
28
00
B9
07
64
00
B9
0A
40
00
B9
7B
00
00
B9
5B
28
00
B9
5D
00
85
50
99
23
7F
00
9F
B2
73
00
FF
2F
00
4D
54
68
64
00
00
00
06
00
00
00
01
00
60
4D
54
72
6B
00
00
00
35
00
FF
03
0D
44
72
75
6D
73
20
20
20
28
42
42
29
00
00
C9
28
00
B9
07
64
00
B9
0A
40
00
B9
7B
00
00
B9
5B
28
00
B9
5D
00
85
50
99
23
7F
00
9F
B2
73
00
FF
2F
00
4D
54
68
64
/
/
MThd 四字节的
type
信息,表示这是一个header trunk
00
00
00
06
/
/
四字节length信息,表示该header trunk后面的长度是
6
字节
00
00
/
/
Format
=
0
, 表示文件只包含一个multi
-
channel track
00
01
/
/
num of tracks
=
1
,和
format
=
0
相对应
00
60
/
/
对delta
-
times的解释,第
15
位为
0
,所以后面
0x60
表示ticks per quarter
-
note,是个音乐概念,不太懂
4D
54
72
6B
/
/
MTrk 四字节的
type
信息,表示这是一个track trunk
00
00
00
35
/
/
四字节length信息,表示该track trunk后面的长度是
53
字节
00
/
/
delta
-
time, 这块数据采用的是variable length quantity
FF
03
0D
44
72
75
6D
73
20
20
20
28
42
42
29
00
/
/
meta
-
event,sequence的名字:Drums (BB)
00
C9
28
/
/
新的program数值为
40
00
B9
07
64
/
/
音量变化
00
B9
0A
40
/
/
Pan?? 不知道是什么
00
B9
7B
00
/
/
所有音符结束
00
B9
5B
28
/
/
效果
1
深度
00
B9
5D
00
/
/
效果
3
深度
85
50
99
23
7F
/
/
0x23
号音符开始
00
9F
B2
73
/
/
0xB2
号音符开始,注意这里B2的最高比特位是
1
,是不合法的
00
FF
2F
00
/
/
track结束
4D
54
68
64
/
/
MThd 四字节的
type
信息,表示这是一个header trunk
00
00
00
06
/
/
四字节length信息,表示该header trunk后面的长度是
6
字节
00
00
/
/
Format
=
0
, 表示文件只包含一个multi
-
channel track
00
01
/
/
num of tracks
=
1
,和
format
=
0
相对应
00
60
/
/
对delta
-
times的解释,第
15
位为
0
,所以后面
0x60
表示ticks per quarter
-
note,是个音乐概念,不太懂
4D
54
72
6B
/
/
MTrk 四字节的
type
信息,表示这是一个track trunk
00
00
00
35
/
/
四字节length信息,表示该track trunk后面的长度是
53
字节
00
/
/
delta
-
time, 这块数据采用的是variable length quantity
FF
03
0D
44
72
75
6D
73
20
20
20
28
42
42
29
00
/
/
meta
-
event,sequence的名字:Drums (BB)
00
C9
28
/
/
新的program数值为
40
00
B9
07
64
/
/
音量变化
00
B9
0A
40
/
/
Pan?? 不知道是什么
00
B9
7B
00
/
/
所有音符结束
00
B9
5B
28
/
/
效果
1
深度
00
B9
5D
00
/
/
效果
3
深度
85
50
99
23
7F
/
/
0x23
号音符开始
00
9F
B2
73
/
/
0xB2
号音符开始,注意这里B2的最高比特位是
1
,是不合法的
00
FF
2F
00
/
/
track结束
C:\Documents
and
Settings\test>
"C:\Program Files\Debugging Tools for Windows\gflags.exe"
-
i IExplore.exe
+
hpa
Current Registry Settings
for
IExplore.exe executable are:
02000000
hpa
-
Enable page heap
C:\Documents
and
Settings\test>
"C:\Program Files\Debugging Tools for Windows\gflags.exe"
-
i IExplore.exe
+
hpa
Current Registry Settings
for
IExplore.exe executable are:
02000000
hpa
-
Enable page heap
(
920.36c
): Access violation
-
code c0000005 (first chance)
First chance exceptions are reported before
any
exception handling.
This exception may be expected
and
handled.
eax
=
00000419
ebx
=
00000073
ecx
=
0073b29f
edx
=
00000000
esi
=
2797f019
edi
=
2797cf60
eip
=
76b5d224
esp
=
2829fe80
ebp
=
2829fea0
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00010246
WINMM!midiOutPlayNextPolyEvent
+
0x1ec
:
76b5d224
8a06
mov al,byte ptr [esi] ds:
0023
:
2797f019
=
??
(
920.36c
): Access violation
-
code c0000005 (first chance)
First chance exceptions are reported before
any
exception handling.
This exception may be expected
and
handled.
eax
=
00000419
ebx
=
00000073
ecx
=
0073b29f
edx
=
00000000
esi
=
2797f019
edi
=
2797cf60
eip
=
76b5d224
esp
=
2829fe80
ebp
=
2829fea0
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00010246
WINMM!midiOutPlayNextPolyEvent
+
0x1ec
:
76b5d224
8a06
mov al,byte ptr [esi] ds:
0023
:
2797f019
=
??
0
:
010
> !heap
-
p
-
a
2797f019
address
2797f019
found
in
_DPH_HEAP_ROOT @
151000
in
busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize
-
VirtAddr VirtSize)
276eaaf8
:
2797ec00
400
-
2797e000
2000
7c918f01
ntdll!RtlAllocateHeap
+
0x00000e64
76b5b2b3
WINMM!winmmAlloc
+
0x00000016
76b5cdee
WINMM!mseOpen
+
0x00000044
76b5d97e
WINMM!mseMessage
+
0x00000029
76b5a17f
WINMM!midiStreamOpen
+
0x00000207
77bd1f7c
midimap!modOpen
+
0x000000f8
77bd29f3
midimap!modMessage
+
0x0000005e
76b5a15e
WINMM!midiStreamOpen
+
0x000001e6
748d56e9
quartz!CMidiOutDevice::DoOpen
+
0x00000026
748d5793
quartz!CMidiOutDevice::amsndOutOpen
+
0x00000059
7484bc64
quartz!CWaveOutFilter::amsndOutOpen
+
0x0000002e
7484bbef
quartz!CWaveOutFilter::DoOpenWaveDevice
+
0x0000007a
7484bdfc
quartz!CWaveOutFilter::OpenWaveDevice
+
0x00000019
7484bcc4
quartz!CWaveOutFilter::Pause
+
0x00000049
7482cf69
quartz!CFilterGraph::Pause
+
0x00000107
7482ce93
quartz!CFGControl::Cue
+
0x00000032
0
:
010
> !heap
-
p
-
a
2797f019
address
2797f019
found
in
_DPH_HEAP_ROOT @
151000
in
busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize
-
VirtAddr VirtSize)
276eaaf8
:
2797ec00
400
-
2797e000
2000
7c918f01
ntdll!RtlAllocateHeap
+
0x00000e64
76b5b2b3
WINMM!winmmAlloc
+
0x00000016
76b5cdee
WINMM!mseOpen
+
0x00000044
76b5d97e
WINMM!mseMessage
+
0x00000029
76b5a17f
WINMM!midiStreamOpen
+
0x00000207
77bd1f7c
midimap!modOpen
+
0x000000f8
77bd29f3
midimap!modMessage
+
0x0000005e
76b5a15e
WINMM!midiStreamOpen
+
0x000001e6
748d56e9
quartz!CMidiOutDevice::DoOpen
+
0x00000026
748d5793
quartz!CMidiOutDevice::amsndOutOpen
+
0x00000059
7484bc64
quartz!CWaveOutFilter::amsndOutOpen
+
0x0000002e
7484bbef
quartz!CWaveOutFilter::DoOpenWaveDevice
+
0x0000007a
7484bdfc
quartz!CWaveOutFilter::OpenWaveDevice
+
0x00000019
7484bcc4
quartz!CWaveOutFilter::Pause
+
0x00000049
7482cf69
quartz!CFilterGraph::Pause
+
0x00000107
7482ce93
quartz!CFGControl::Cue
+
0x00000032
0
:
010
> kb
ChildEBP RetAddr Args to Child
2829fea0
76b5d2e5
b2ceead0
76b5d296
00000010
WINMM!midiOutPlayNextPolyEvent
+
0x1ec
2829feb4
76b454e3
00000010
00000000
00000e7c
WINMM!midiOutTimerTick
+
0x4f
2829fedc
76b5adfe
76b5d296
00000003
00000010
WINMM!DriverCallback
+
0x5c
2829ff18
76b5af02
00000010
015f0000
00000000
WINMM!TimerCompletion
+
0xf4
2829ffb4
7c80b713
00000000
015f0000
015f0000
WINMM!timeThread
+
0x53
2829ffec
00000000
76b5aeaf
00000000
00000000
kernel32!BaseThreadStart
+
0x37
0
:
010
> kb
ChildEBP RetAddr Args to Child
2829fea0
76b5d2e5
b2ceead0
76b5d296
00000010
WINMM!midiOutPlayNextPolyEvent
+
0x1ec
2829feb4
76b454e3
00000010
00000000
00000e7c
WINMM!midiOutTimerTick
+
0x4f
2829fedc
76b5adfe
76b5d296
00000003
00000010
WINMM!DriverCallback
+
0x5c
2829ff18
76b5af02
00000010
015f0000
00000000
WINMM!TimerCompletion
+
0xf4
2829ffb4
7c80b713
00000000
015f0000
015f0000
WINMM!timeThread
+
0x53
2829ffec
00000000
76b5aeaf
00000000
00000000
kernel32!BaseThreadStart
+
0x37
0
:
010
> lm vm WINMM
start end module name
76b40000
76b6d000
WINMM (pdb symbols) E:\symbols\dll\winmm.pdb
Loaded symbol image
file
: C:\WINDOWS\system32\WINMM.dll
Image path: C:\WINDOWS\system32\WINMM.dll
...
0
:
010
> lm vm WINMM
start end module name
76b40000
76b6d000
WINMM (pdb symbols) E:\symbols\dll\winmm.pdb
Loaded symbol image
file
: C:\WINDOWS\system32\WINMM.dll
Image path: C:\WINDOWS\system32\WINMM.dll
...
v20
=
(char
*
)(v19
+
v16);
v21
=
*
v20;
/
/
异常发生位置
v20
=
(char
*
)(v19
+
v16);
v21
=
*
v20;
/
/
异常发生位置
v16
=
*
(_DWORD
*
)(wParam
+
0x84
);
v19
=
(wParam_3a
+
((v17 &
0xF
) <<
7
))
/
2
;
v16
=
*
(_DWORD
*
)(wParam
+
0x84
);
v19
=
(wParam_3a
+
((v17 &
0xF
) <<
7
))
/
2
;
0
:
010
> dd b2ceeb54 L1
b2ceeb54 ????????
0
:
010
> dd b2ceeb54 L1
b2ceeb54 ????????
0
:
007
> bu
76B5D038
0
:
007
> g
Breakpoint
0
hit
eax
=
00000000
ebx
=
ffffffff ecx
=
7ffdf000
edx
=
27db4f70
esi
=
27db4f60
edi
=
27db4fb8
eip
=
76b5d038
esp
=
0013e5b0
ebp
=
0013e5dc
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00000246
WINMM!midiOutPlayNextPolyEvent:
76b5d038
8bff
mov edi,edi
0
:
000
> g
Breakpoint
0
hit
eax
=
00000000
ebx
=
ffffffff ecx
=
7ffd4000
edx
=
27db4f70
esi
=
27db4f60
edi
=
27db4fb8
eip
=
76b5d038
esp
=
2829fea4
ebp
=
2829fedc
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00000246
WINMM!midiOutPlayNextPolyEvent:
76b5d038
8bff
mov edi,edi
0
:
007
> bu
76B5D038
0
:
007
> g
Breakpoint
0
hit
eax
=
00000000
ebx
=
ffffffff ecx
=
7ffdf000
edx
=
27db4f70
esi
=
27db4f60
edi
=
27db4fb8
eip
=
76b5d038
esp
=
0013e5b0
ebp
=
0013e5dc
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00000246
WINMM!midiOutPlayNextPolyEvent:
76b5d038
8bff
mov edi,edi
0
:
000
> g
Breakpoint
0
hit
eax
=
00000000
ebx
=
ffffffff ecx
=
7ffd4000
edx
=
27db4f70
esi
=
27db4f60
edi
=
27db4fb8
eip
=
76b5d038
esp
=
2829fea4
ebp
=
2829fedc
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00000246
WINMM!midiOutPlayNextPolyEvent:
76b5d038
8bff
mov edi,edi
0
:
010
> dd esp l4
2829fea4
76b5d2e5
27db4f60
76b5d296
00000010
0
:
010
> dd esp l4
2829fea4
76b5d2e5
27db4f60
76b5d296
00000010
0
:
010
> db
27db4f60
27db4f60
00
00
00
00
b8 cf da
27
-
80
00
00
00
00
00
00
00
.......'........
27db4f70
e0
6f
5b
04
00
00
00
00
-
01
00
00
00
b8
06
00
00
.o[.............
27db4f80
00
00
00
00
00
00
00
00
-
34
12
34
12
60
00
00
00
........
4.4
.`...
27db4f90
20
a1
07
00
00
00
00
00
-
00
00
00
00
c0 af de
27
..............'
27db4fa0
c0 af de
27
a4
9e
b5
76
-
00
00
03
00
00
00
00
00
...'...v........
27db4fb0
00
00
00
00
b9
00
00
00
-
00
00
00
00
00
00
00
00
................
27db4fc0
a4
11
a6
02
00
77
01
00
-
20
a1
07
00
02
00
00
00
.....w.. .......
27db4fd0
9d
cd b5
76
d0
02
00
00
-
00
00
00
00
00
00
00
00
...v............
0
:
010
> db
27db4f60
27db4f60
00
00
00
00
b8 cf da
27
-
80
00
00
00
00
00
00
00
.......'........
27db4f70
e0
6f
5b
04
00
00
00
00
-
01
00
00
00
b8
06
00
00
.o[.............
27db4f80
00
00
00
00
00
00
00
00
-
34
12
34
12
60
00
00
00
........
4.4
.`...
27db4f90
20
a1
07
00
00
00
00
00
-
00
00
00
00
c0 af de
27
..............'
27db4fa0
c0 af de
27
a4
9e
b5
76
-
00
00
03
00
00
00
00
00
...'...v........
27db4fb0
00
00
00
00
b9
00
00
00
-
00
00
00
00
00
00
00
00
................
27db4fc0
a4
11
a6
02
00
77
01
00
-
20
a1
07
00
02
00
00
00
.....w.. .......
27db4fd0
9d
cd b5
76
d0
02
00
00
-
00
00
00
00
00
00
00
00
...v............
void __stdcall midiOutPlayNextPolyEvent(WPARAM wParam) {
if
( !
*
(wParam
+
0x34
) ) {
while
(
1
) {
while
(
1
) {
v2
=
*
(wParam
+
0x3C
);
if
( !v2 )
return
;
if
( midiOutScheduleNextEvent(wParam) )
break
;
midiOutDequeueAndCallback(wParam);
}
...
v2[
9
]
+
=
4
;
v7
=
v2[
9
];
...
v7
+
=
4
;
...
v8
=
*
(v7
+
wParama);
...
v10
=
v8 &
0xFFFFFF
;
...
v16
=
*
(wParam
+
0x84
);
...
}
v17
=
v10;
if
( (v10 &
0x80u
) !
=
0
) {
wParam_3a
=
BYTE1(v10);
...
}
else
{
v17
=
*
(wParam
+
0x54
);
wParam_3a
=
v10;
...
}
v33
=
v17 &
0xF0
;
if
( (v17 &
0xF0
)
=
=
0x90
|| (v17 &
0xF0
)
=
=
0x80
) {
v19
=
(wParam_3a
+
((v17 &
0xF
) <<
7
))
/
2
;
if
...
v20
=
(v19
+
v16);
v21
=
*
v20;
/
/
异常发生位置
v22
=
*
v20;
if
...
}
...
}
}
void __stdcall midiOutPlayNextPolyEvent(WPARAM wParam) {
if
( !
*
(wParam
+
0x34
) ) {
while
(
1
) {
while
(
1
) {
v2
=
*
(wParam
+
0x3C
);
if
( !v2 )
return
;
if
( midiOutScheduleNextEvent(wParam) )
break
;
midiOutDequeueAndCallback(wParam);
}
...
v2[
9
]
+
=
4
;
v7
=
v2[
9
];
...
v7
+
=
4
;
...
v8
=
*
(v7
+
wParama);
...
v10
=
v8 &
0xFFFFFF
;
...
v16
=
*
(wParam
+
0x84
);
...
}
v17
=
v10;
if
( (v10 &
0x80u
) !
=
0
) {
wParam_3a
=
BYTE1(v10);
...
}
else
{
v17
=
*
(wParam
+
0x54
);
wParam_3a
=
v10;
...
}
v33
=
v17 &
0xF0
;
if
( (v17 &
0xF0
)
=
=
0x90
|| (v17 &
0xF0
)
=
=
0x80
) {
v19
=
(wParam_3a
+
((v17 &
0xF
) <<
7
))
/
2
;
if
...
v20
=
(v19
+
v16);
v21
=
*
v20;
/
/
异常发生位置
v22
=
*
v20;
if
...
}
...
}
}
.text:
76B5D041
8B
7D
08
mov edi, [ebp
+
wParam]
/
/
wParam
.text:
76B5D050
8B
77
3C
mov esi, [edi
+
3Ch
]
/
/
v2
.text:
76B5D09D
83
C3
04
add ebx,
4
/
/
v7
.text:
76B5D0B5
8B
0C
03
mov ecx, [ebx
+
eax]
/
/
v8
.text:
76B5D0C3
81
E1 FF FF FF
00
and
ecx,
0FFFFFFh
/
/
v10
.text:
76B5D1C7
8A
C1 mov al, cl
/
/
v17
=
v10
.text:
76B5D1CD
8A
47
54
mov al, [edi
+
54h
]
/
/
v17
.text:
76B5D1EB
88
55
0B
mov byte ptr [ebp
+
wParam
+
3
], dl
/
/
wParam_3a
.text:
76B5D1D0
88
4D
0B
mov byte ptr [ebp
+
wParam
+
3
], cl
/
/
wParam_3a
.text:
76B5D212
D1 F8 sar eax,
1
/
/
v19
.text:
76B5D1B9
8B
B7
84
00
00
00
mov esi, [edi
+
84h
]
/
/
v16
.text:
76B5D21E
03
F0 add esi, eax
/
/
v20
.text:
76B5D041
8B
7D
08
mov edi, [ebp
+
wParam]
/
/
wParam
.text:
76B5D050
8B
77
3C
mov esi, [edi
+
3Ch
]
/
/
v2
.text:
76B5D09D
83
C3
04
add ebx,
4
/
/
v7
.text:
76B5D0B5
8B
0C
03
mov ecx, [ebx
+
eax]
/
/
v8
.text:
76B5D0C3
81
E1 FF FF FF
00
and
ecx,
0FFFFFFh
/
/
v10
.text:
76B5D1C7
8A
C1 mov al, cl
/
/
v17
=
v10
.text:
76B5D1CD
8A
47
54
mov al, [edi
+
54h
]
/
/
v17
.text:
76B5D1EB
88
55
0B
mov byte ptr [ebp
+
wParam
+
3
], dl
/
/
wParam_3a
.text:
76B5D1D0
88
4D
0B
mov byte ptr [ebp
+
wParam
+
3
], cl
/
/
wParam_3a
.text:
76B5D212
D1 F8 sar eax,
1
/
/
v19
.text:
76B5D1B9
8B
B7
84
00
00
00
mov esi, [edi
+
84h
]
/
/
v16
.text:
76B5D21E
03
F0 add esi, eax
/
/
v20
断点 输出 变量
76B5D044
edi wParam
76B5D053
esi v2
76B5D0A0
ebx v7
76B5D0B8
ecx v8
76B5D0C9
ecx v10
76B5D1C9
al v17
76B5D1D0
al v17
76B5D1EB
dl wParam_3a
76B5D1D0
cl wParam_3a
76B5D214
eax v19
76B5D1BF
esi v16
76B5D220
esi v20
断点 输出 变量
76B5D044
edi wParam
76B5D053
esi v2
76B5D0A0
ebx v7
76B5D0B8
ecx v8
76B5D0C9
ecx v10
76B5D1C9
al v17
76B5D1D0
al v17
76B5D1EB
dl wParam_3a
76B5D1D0
cl wParam_3a
76B5D214
eax v19
76B5D1BF
esi v16
76B5D220
esi v20
(
920.ffc
): Break instruction exception
-
code
80000003
(first chance)
eax
=
7ffdd000
ebx
=
00000001
ecx
=
00000002
edx
=
00000003
esi
=
00000004
edi
=
00000005
eip
=
7c90120e
esp
=
044affcc
ebp
=
044afff4
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
0038
gs
=
0000
efl
=
00000246
ntdll!DbgBreakPoint:
7c90120e
cc
int
3
0
:
007
> bu
76B5D038
0
:
007
> g
Breakpoint
0
hit
eax
=
00000000
ebx
=
ffffffff ecx
=
7ffdf000
edx
=
27db4f70
esi
=
27db4f60
edi
=
27db4fb8
eip
=
76b5d038
esp
=
0013e5b0
ebp
=
0013e5dc
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00000246
WINMM!midiOutPlayNextPolyEvent:
76b5d038
8bff
mov edi,edi
0
:
000
> bp
76B5D044
".echo 'wParam';r edi;g;"
0
:
000
> bp
76B5D053
".echo 'v2';r esi;g;"
0
:
000
> bp
76B5D0A0
".echo 'v7';r ebx;g;"
0
:
000
> bp
76B5D0B8
".echo 'v8';r ecx;g;"
0
:
000
> bp
76B5D0C9
".echo 'v10';r ecx;g;"
0
:
000
> bp
76B5D1C9
".echo 'v17';r al;g;"
0
:
000
> bp
76B5D1D0
".echo 'v17';r al;.echo 'wParam_3a';r cl;g;"
0
:
000
> bp
76B5D1EB
".echo 'wParam_3a';r dl;g;"
0
:
000
> bp
76B5D214
".echo 'v19';r eax;g;"
0
:
000
> bp
76B5D1BF
".echo 'v16';r esi;g;"
0
:
000
> bp
76B5D220
".echo 'v20';r esi;g;"
0
:
000
> g
(
920.ffc
): Break instruction exception
-
code
80000003
(first chance)
eax
=
7ffdd000
ebx
=
00000001
ecx
=
00000002
edx
=
00000003
esi
=
00000004
edi
=
00000005
eip
=
7c90120e
esp
=
044affcc
ebp
=
044afff4
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
0038
gs
=
0000
efl
=
00000246
ntdll!DbgBreakPoint:
7c90120e
cc
int
3
0
:
007
> bu
76B5D038
0
:
007
> g
Breakpoint
0
hit
eax
=
00000000
ebx
=
ffffffff ecx
=
7ffdf000
edx
=
27db4f70
esi
=
27db4f60
edi
=
27db4fb8
eip
=
76b5d038
esp
=
0013e5b0
ebp
=
0013e5dc
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00000246
WINMM!midiOutPlayNextPolyEvent:
76b5d038
8bff
mov edi,edi
0
:
000
> bp
76B5D044
".echo 'wParam';r edi;g;"
0
:
000
> bp
76B5D053
".echo 'v2';r esi;g;"
0
:
000
> bp
76B5D0A0
".echo 'v7';r ebx;g;"
0
:
000
> bp
76B5D0B8
".echo 'v8';r ecx;g;"
0
:
000
> bp
76B5D0C9
".echo 'v10';r ecx;g;"
0
:
000
> bp
76B5D1C9
".echo 'v17';r al;g;"
0
:
000
> bp
76B5D1D0
".echo 'v17';r al;.echo 'wParam_3a';r cl;g;"
0
:
000
> bp
76B5D1EB
".echo 'wParam_3a';r dl;g;"
0
:
000
> bp
76B5D214
".echo 'v19';r eax;g;"
0
:
000
> bp
76B5D1BF
".echo 'v16';r esi;g;"
0
:
000
> bp
76B5D220
".echo 'v20';r esi;g;"
0
:
000
> g
if
( (v17 &
0xF0
)
=
=
0x90
|| (v17 &
0xF0
)
=
=
0x80
)
if
( v33
=
=
(char)
0x80
|| !(_BYTE)v18 ) {
/
/
这里执行一些操作,然后直接跳出了上层循环
}
/
/
异常发生在这里
if
( (v17 &
0xF0
)
=
=
0x90
|| (v17 &
0xF0
)
=
=
0x80
)
if
( v33
=
=
(char)
0x80
|| !(_BYTE)v18 ) {
/
/
这里执行一些操作,然后直接跳出了上层循环
}
/
/
异常发生在这里
v20
=
(char
*
)(v19
+
v16);
v20
=
(char
*
)(v19
+
v16);
v19
=
(wParam_3a
+
((v17 &
0xF
) <<
7
))
/
2
;
v19
=
(wParam_3a
+
((v17 &
0xF
) <<
7
))
/
2
;
0
:
010
> !heap
-
p
-
a
27db6c00
address
27db6c00
found
in
_DPH_HEAP_ROOT @
151000
in
busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize
-
VirtAddr VirtSize)
277ca4b8
:
27db6c00
400
-
27db6000
2000
7c918f01
ntdll!RtlAllocateHeap
+
0x00000e64
76b5b2b3
WINMM!winmmAlloc
+
0x00000016
76b5cdee
WINMM!mseOpen
+
0x00000044
76b5d97e
WINMM!mseMessage
+
0x00000029
76b5a17f
WINMM!midiStreamOpen
+
0x00000207
...
0
:
010
> !heap
-
p
-
a
27db6c00
address
27db6c00
found
in
_DPH_HEAP_ROOT @
151000
in
busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize
-
VirtAddr VirtSize)
277ca4b8
:
27db6c00
400
-
27db6000
2000
7c918f01
ntdll!RtlAllocateHeap
+
0x00000e64
76b5b2b3
WINMM!winmmAlloc
+
0x00000016
76b5cdee
WINMM!mseOpen
+
0x00000044
76b5d97e
WINMM!mseMessage
+
0x00000029
76b5a17f
WINMM!midiStreamOpen
+
0x00000207
...
(b30.
8d8
): Access violation
-
code c0000005 (first chance)
First chance exceptions are reported before
any
exception handling.
This exception may be expected
and
handled.
eax
=
00000400
ebx
=
00000073
ecx
=
0073809f
edx
=
00000000
esi
=
27c61000
edi
=
04ad4f60
eip
=
76b5d224
esp
=
27f5fe80
ebp
=
27f5fea0
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00010246
WINMM!midiOutPlayNextPolyEvent
+
0x1ec
:
76b5d224
8a06
mov al,byte ptr [esi] ds:
0023
:
27c61000
=
??
0
:
010
> !heap
-
p
-
a
27c61000
address
27c61000
found
in
_DPH_HEAP_ROOT @
151000
in
busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize
-
VirtAddr VirtSize)
272ddec8
:
27c60c00
400
-
27c60000
2000
(b30.
8d8
): Access violation
-
code c0000005 (first chance)
First chance exceptions are reported before
any
exception handling.
This exception may be expected
and
handled.
eax
=
00000400
ebx
=
00000073
ecx
=
0073809f
edx
=
00000000
esi
=
27c61000
edi
=
04ad4f60
eip
=
76b5d224
esp
=
27f5fe80
ebp
=
27f5fea0
iopl
=
0
nv up ei pl zr na pe nc
cs
=
001b
ss
=
0023
ds
=
0023
es
=
0023
fs
=
003b
gs
=
0000
efl
=
00010246
WINMM!midiOutPlayNextPolyEvent
+
0x1ec
:
76b5d224
8a06
mov al,byte ptr [esi] ds:
0023
:
27c61000
=
??
0
:
010
> !heap
-
p
-
a
27c61000
address
27c61000
found
in
_DPH_HEAP_ROOT @
151000
in
busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize
-
VirtAddr VirtSize)
272ddec8
:
27c60c00
400
-
27c60000
2000
.text:
76B5CDE4
68
00
04
00
00
push
400h
; dwBytes
.text:
76B5CDE9
E8 AF E4 FF FF call _winmmAlloc@
4
; winmmAlloc(x)
.text:
76B5CDE4
68
00
04
00
00
push
400h
; dwBytes
.text:
76B5CDE9
E8 AF E4 FF FF call _winmmAlloc@
4
; winmmAlloc(x)
v21
=
*
v20;
/
/
异常发生位置
v22
=
*
v20;
if
( (wParam_3a &
1
) !
=
0
) {
if
( (v22 &
0xF0
) !
=
0xF0
) {
v23
=
v21
+
0x10
;
LABEL_39:
*
v20
=
v23;
goto LABEL_46;
}
}
else
if
( (v22 &
0xF
) !
=
0xF
) {
v23
=
v21
+
1
;
goto LABEL_39;
}
v21
=
*
v20;
/
/
异常发生位置
v22
=
*
v20;
if
( (wParam_3a &
1
) !
=
0
) {
if
( (v22 &
0xF0
) !
=
0xF0
) {
v23
=
v21
+
0x10
;
LABEL_39:
*
v20
=
v23;
goto LABEL_46;
}
}
else
if
( (v22 &
0xF
) !
=
0xF
) {
v23
=
v21
+
1
;
goto LABEL_39;
}
if
( (wParam_3a &
1
) !
=
0
)
/
/
判断失败
else
if
( (v22 &
0xF
) !
=
0xF
)
/
/
判断成功,执行
v23
=
v21
+
1
;
goto LABEL_39;
/
/
跳转进行写操作:
*
v20
=
v23;
if
( (wParam_3a &
1
) !
=
0
)
/
/
判断失败
else
if
( (v22 &
0xF
) !
=
0xF
)
/
/
判断成功,执行
v23
=
v21
+
1
;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!