金山WPS Office遠端堆溢位漏洞分析

0x01 漏洞描述

WPS Office是適用於Microsoft Windows,macOS,Linux,iOS和Android的辦公軟體,由總部位於珠海的中國軟體開發商金山軟體公司開發。WPS Office由三個主要元件組成:WPS Writer,WPS Presentation和WPS Spreadsheet。個人基本版本可以免費使用,WPS Office軟體中存在一個遠端執行程式碼漏洞,是當Office軟體在分析特製Office檔案時不正確地處理記憶體中的物件時引起的。成功利用此漏洞的攻擊者可以在當前使用者的上下文中執行任意程式碼。漏洞可能會導致拒絕服務,易受攻擊的產品WPS Office,影響版本11。2。0。9453。

0x02 漏洞分析

在WPS Office中用於影象格式解析的Qt模組中發現存在堆溢位。嵌入WPS office的特製影象檔案可能會觸發此漏洞。開啟特製的文件檔案時,觸發訪問衝突。EDX指向陣列的指標,而EAX是指向陣列的索引。

0:000> g

(c50。b4): Access violation - code c0000005 (first chance)

First chance exceptions are reported before any exception handling。

This exception may be expected and handled。

eax=000000c0 ebx=006f1c48 ecx=cd2aefbc edx=cd2c6f80 esi=2ed7ae18 edi=0000001c

eip=6ba13321 esp=006f1b44 ebp=006f1b44 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210202

QtCore4!QMatrix::dy+0x48a8:

6ba13321 8b448210 mov eax,dword ptr [edx+eax*4+10h] ds:002b:cd2c7290=????????

崩潰是如何觸發的?讓我們看一下PNG標頭格式。

00029E30 FF 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 ‰PNG……。。IHD

00029E40 52 00 00 02 80 00 00 01 C6 04 03 00 00 00 16 0A R。。。�。。。 ……。

00029E50 27 FC 00 00 00 04 67 41 4D 41 00 00 B1 88 95 98 ‘ü。。。。gAMA。。±

00029E60 F4 A6 00 00 00 30 50 4C 54 45 00 00 00 80 00 00 。。。0PLTE。。。�。。

00029E70 00 80 00 80 80 00 00 00 80 80 00 80 00 80 80 80 。�。��。。。��。�。���

00029E80 80 80 C0 C0 C0 FF 00 00 00 FF 00 FF FF 00 00 00 �� 。。。 。 。。。

00029E90 FF FF 00 FF 00 FF FF FF FF FF 7B 1F B1 C4 00 00 。 。 {。± 。。

從偏移量0x29E31開始-0x29E34是PNG檔案格式的簽名標頭。PNG標頭檔案的結構:

PNG signature ——> IHDR ——> gAMA ——> PLTE ——> pHYs ——> IDAT ——> IEND

在這種情況下,當WPS Office Suite中使用的QtCore庫解析PLTE結構並觸發堆溢位時,該漏洞位於Word文件中的嵌入式PNG檔案中。在偏移量0x29E82到0x29E85處,調色盤的解析失敗,從而觸發了堆中的記憶體損壞。崩潰觸發之前的堆疊跟蹤:

00 00ee1790 6b8143ef QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a71

01 00ee17f0 6b814259 QtCore4!QBrush::setMatrix+0x234

02 00ee58d4 6b8249a4 QtCore4!QBrush::setMatrix+0x9e

03 00ee58ec 6b80cc84 QtCore4!QImage::rect+0x22b

04 00ee5908 6b857ccc QtCore4!QTransform::inverted+0xec8

05 00ee629c 6b81c55b QtCore4!QSvgFillStyle::setFillOpacity+0x1b59

06 00ee6480 6b896844 QtCore4!QPainter::drawPixmap+0x1c98

07 00ee6574 6d1e0fbd QtCore4!QPainter::drawImage+0x325

08 00ee6594 6d0dd155 kso!GdiDrawHoriLineIAlt+0x11a1a

在QtCore4解析嵌入式影象之前,我們可以看到來自KSO模組的最後一次呼叫,試圖處理影象kso!GdiDrawHoriLineIAlt。使用IDA Pro分解應用程式來分析發生異常的函式。最後的崩潰路徑如下(WinDBG結果):

QtCore4!QMatrix::dy+0x48a8:

6ba13321 8b448210 mov eax,dword ptr [edx+eax*4+10h] ds:002b:cd2c7290=????????

在IDA Pro中開啟時,我們可以按以下方式反彙編該函式:

。text:67353315 push ebp

。text:67353316 mov ebp, esp

。text:67353318 movzx eax, byte ptr [ecx+edx] ; crash here

。text:6735331C mov ecx, [ebp+arg_0]

。text:6735331F mov edx, [ecx]

。text:67353321 mov eax, [edx+eax*4+10h]

。text:67353325 mov ecx, eax

使用crashs轉儲中的資訊,我們知道應用程式在0x67353321(mov eax,[edx + eax * 4 + 10h])處觸發了訪問衝突。我們可以看到EAX暫存器由0xc0值控制。因此,從這裡我們可以根據導致異常的指令對暫存器的狀態進行一些假設。需要注意的重要一點是,在發生異常之前,我們可以看到ECX(0xc0)中包含的值被寫入到以下指令所定義的任意位置:

mov ecx, [ebp+arg_0]

此外,我們注意到,在我們的故障指令之外,EBP的偏移量儲存在ECX暫存器中。我們在前面提到的指令(偏移量為0x6ba1331c)上設定了一個斷點,以觀察記憶體。斷點觸發後,我們可以看到第一個值c45adfbc指向另一個指標,該指標應該是指向陣列的指標。

Breakpoint 0 hit

eax=0000000f ebx=004f1b40 ecx=d3544100 edx=0000001c esi=d1200e18 edi=0000001c

eip=6ba1331c esp=004f1a34 ebp=004f1a34 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202

QtCore4!QMatrix::dy+0x48a3:

6ba1331c 8b4d08 mov ecx,dword ptr [ebp+8] ss:002b:004f1a3c=c45adfbc

0:000> dc ebp+8

004f1a3c c45adfbc 00000048 00000000 6f13830f 。。Z。H……。。。。o

004f1a4c 004f5cc8 00000000 00000000 00000000 。\O…………。

004f1a5c 00000000 004f65a0 004f662c 00000000 。。。。。eO。,fO。。。。。

004f1a6c 779eae8e 00000000 00000001 3f800000 。。。w……。。。。。?

004f1a7c 3f800000 3f31e4f8 3f800000 3f800000 。。。?。。1?。。。?。。。?

004f1a8c 3f800000 3f31e4f8 3f800000 3de38800 。。。?。。1?。。。?。。。=

004f1a9c 3de38800 3d9e1c8a 3c834080 004f3c00 。。。=。。。=。@。

004f1aac 4101c71c 6ba13315 3f800000 4081c71c 。。。A。3。k。。。?。。。@

從c45adfbc觀察記憶體引用,發現另一個指標。第一個值ab69cf80始終表示為指向它所引用的任何地方的指標。指標ab69cf80基本上是我們指標的索引陣列。

0:000> dc c45adfbc

c45adfbc ab69cf80 d3544100 00000003 00000280 。。i。。AT……。。。

c45adfcc 0000055a 00000012 c0c0c0c0 1c3870e2 Z…………p8。

c45adfdc 40ad870e 1c3870e2 40ad870e 00000000 。。。@。p8。。。。@。。。。

c45adfec 00000000 c0c0c0c1 6c1d12c0 00000000 ……。。。。。l。。。。

c45adffc c0c0c0c0 ???????? ???????? ???????? 。。。。????????????

c45ae00c ???????? ???????? ???????? ???????? ????????????????

c45ae01c ???????? ???????? ???????? ???????? ????????????????

c45ae02c ???????? ???????? ???????? ???????? ????????????????

0:000> dc ab69cf80

ab69cf80 00000001 0000001c 00000010 00000001 …………。。。。 // 0000001c is overwritten in the register EDX and EDI before we trigger crash

ab69cf90 ff000000 ff800000 ff008000 ff808000 …………。。。。

ab69cfa0 ff000080 ff800080 ff008080 ff808080 …………。。。。

ab69cfb0 ffc0c0c0 ffff0000 ff00ff00 ffffff00 …………。。。。 // ffc0c0c0 where it will be stored in EAX after crash, at the moment it only takes 0xf value in EAX

ab69cfc0 ff0000ff ffff00ff ff00ffff ffffffff …………。。。。

ab69cfd0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

ab69cfe0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

ab69cff0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

因為我們知道崩潰的路徑,所以可以使用下面的命令簡單地設定一個斷點。該命令將獲得指標值“ edx + eax * 4 + 10”,並檢查其是否滿足0xc0。

bp 6ba13321 “。if (poi(edx+eax*4+10) == 0xc0) {} 。else ”

0:000> g

eax=000000c0 ebx=004f1b40 ecx=c45adfbc edx=ab69cf80 esi=d1200e18 edi=0000001c

eip=6ba13321 esp=004f1a34 ebp=004f1a34 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202

QtCore4!QMatrix::dy+0x48a8:

6ba13321 8b448210 mov eax,dword ptr [edx+eax*4+10h] ds:002b:ab69d290=????????

觀察堆疊,可以看到以下執行:

004f1a38 6ba3cb98 QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a74

004f1a3c c45adfbc

004f1a40 00000048

004f1a44 00000000

004f1a48 6f13830f verifier!DphCommitMemoryForPageHeap+0x16f

004f1a4c 004f5cc8

004f1a50 00000000

004f1a54 00000000

004f1a58 00000000

004f1a5c 00000000

004f1a60 004f65a0

004f1a64 004f662c

004f1a68 00000000

004f1a6c 779eae8e ntdll!RtlAllocateHeap+0x3e

如果我們反彙編6ba3cb98,則可以看到以下反彙編程式碼,真正的漏洞根本原因在於此程式碼。

6ba3cb89 8b96b4000000 mov edx,dword ptr [esi+0B4h]

6ba3cb8f 8b4df4 mov ecx,dword ptr [ebp-0Ch]

6ba3cb92 52 push edx

6ba3cb93 8bd7 mov edx,edi

6ba3cb95 ff5580 call dword ptr [ebp-80h]

6ba3cb98 8b4e7c mov ecx,dword ptr [esi+7Ch]

C pseudo code

grad = *(&ptr_grad);

if ( grad > 0。0099999998 )

{

input_value = grad_size(check, size, input);

ptr_grad = *(input);

。。。 cut here 。。。

我們在6ba3cb89地址上設定斷點並觀察ESI + 0xB4,我們可以看到一個指標指向另一個位置:

0:000> r

eax=00000000 ebx=00791878 ecx=00000005 edx=00793938 esi=cb07de18 edi=0000001c

eip=6ba3cb89 esp=00791780 ebp=00791870 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202

QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a65:

6ba3cb89 8b96b4000000 mov edx,dword ptr [esi+0B4h] ds:002b:cb07decc=cf69afbc

0:000> dc esi+0B4h

cb07decc cf69afbc c0c0c000 00000000 00000100 。。i…………。

cb07dedc c0c0c0c0 00000000 00000000 00000000 …………。。。。

cb07deec 00000000 00000000 00000000 00000000 …………。。。。

cb07defc 00000000 cf030fd0 00000000 00000000 …………。。。。

cb07df0c 00000000 00000000 00000000 00000000 …………。。。。

cb07df1c c0c0c0c0 00000000 3ff00000 00000000 ……。。。。。?。。。。

cb07df2c 00000000 00000000 00000000 00000000 …………。。。。

cb07df3c 00000000 00000000 3ff00000 00000000 ……。。。。。?。。。。

0:000> dc cf69afbc

cf69afbc c88baf80 d1326100 00000003 00000280 。。。。。a2……。。。

cf69afcc 0000055f 00000012 c0c0c0c0 1c3870e2 _…………p8。

cf69afdc 40ad870e 1c3870e2 40ad870e 00000000 。。。@。p8。。。。@。。。。

cf69afec 00000000 c0c0c0c1 6c1d12c0 00000000 ……。。。。。l。。。。

cf69affc c0c0c0c0 ???????? ???????? ???????? 。。。。????????????

cf69b00c ???????? ???????? ???????? ???????? ????????????????

cf69b01c ???????? ???????? ???????? ???????? ????????????????

cf69b02c ???????? ???????? ???????? ???????? ????????????????

0:000> dc c88baf80

c88baf80 00000001 0000001c 00000010 00000001 …………。。。。

c88baf90 ff000000 ff800000 ff008000 ff808000 …………。。。。

c88bafa0 ff000080 ff800080 ff008080 ff808080 …………。。。。

c88bafb0 ffc0c0c0 ffff0000 ff00ff00 ffffff00 …………。。。。

c88bafc0 ff0000ff ffff00ff ff00ffff ffffffff …………。。。。

c88bafd0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

c88bafe0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

c88baff0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

從這裡我們可以知道程式碼實際上沒有從指標釋放任何東西。一旦移至EDX,EDX將保留指向索引陣列的指標:

eax=00000000 ebx=00791878 ecx=00000005 edx=cf69afbc esi=cb07de18 edi=0000001c

eip=6ba3cb8f esp=00791780 ebp=00791870 iopl=0 nv up ei pl nz na po nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202

QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a6b:

6ba3cb8f 8b4df4 mov ecx,dword ptr [ebp-0Ch] ss:002b:00791864=d1326100

0:000> dc cf69afbc

cf69afbc c88baf80 d1326100 00000003 00000280 。。。。。a2……。。。

cf69afcc 0000055f 00000012 c0c0c0c0 1c3870e2 _…………p8。

cf69afdc 40ad870e 1c3870e2 40ad870e 00000000 。。。@。p8。。。。@。。。。

cf69afec 00000000 c0c0c0c1 6c1d12c0 00000000 ……。。。。。l。。。。

cf69affc c0c0c0c0 ???????? ???????? ???????? 。。。。????????????

cf69b00c ???????? ???????? ???????? ???????? ????????????????

cf69b01c ???????? ???????? ???????? ???????? ????????????????

cf69b02c ???????? ???????? ???????? ???????? ????????????????

0:000> dc c88baf80

c88baf80 00000001 0000001c 00000010 00000001 …………。。。。

c88baf90 ff000000 ff800000 ff008000 ff808000 …………。。。。

c88bafa0 ff000080 ff800080 ff008080 ff808080 …………。。。。

c88bafb0 ffc0c0c0 ffff0000 ff00ff00 ffffff00 …………。。。。

c88bafc0 ff0000ff ffff00ff ff00ffff ffffffff …………。。。。

c88bafd0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

c88bafe0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

c88baff0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 …………。。。。

崩潰後的堆疊跟蹤:

0:000> kvL

# ChildEBP RetAddr Args to Child

00 012f18d4 6ba3cb98 cc53afbc 00000048 00000000 QtCore4!QMatrix::dy+0x48a8

01 012f19d0 6b8143ef 00000000 012f1b78 012f1a5c QtCore4!path_gradient_span_gen::path_gradient_span_gen+0x6a74

02 012f1a30 6b814259 0000002e 012f5bd0 00000000 QtCore4!QBrush::setMatrix+0x234

03 012f5b14 6b8249a4 0000003b 012f5b68 cc780e18 QtCore4!QBrush::setMatrix+0x9e

04 012f5b2c 6b80cc84 0000003b 012f5b68 cc780e18 QtCore4!QImage::rect+0x22b

05 012f5b48 6b857ccc 0000003b 012f5b68 cc780e18 QtCore4!QTransform::inverted+0xec8

06 012f64dc 6b81c55b 00000000 003c0000 00000000 QtCore4!QSvgFillStyle::setFillOpacity+0x1b59

07 012f66c0 6b896844 012f6724 cc818ff0 0000001c QtCore4!QPainter::drawPixmap+0x1c98

08 012f67b4 6d1e0fbd 012f69ec 012f66d4 012f6864 QtCore4!QPainter::drawImage+0x325

09 012f67d4 6d0dd155 012f6a54 012f69ec 012f6864 kso!GdiDrawHoriLineIAlt+0x11a1a

0a 012f67ec 6d0c8d88 012f69ec 012f68e0 012f6864 kso!kpt::PainterExt::drawBitmap+0x23

堆分析:

0:000> !heap -p -a cc53afbc

address cc53afbc found in

_DPH_HEAP_ROOT @ 6731000

in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)

cc36323c: cc53afa8 58 - cc53a000 2000

6f13ab70 verifier!AVrfDebugPageHeapAllocate+0x00000240

77a9909b ntdll!RtlDebugAllocateHeap+0x00000039

779ebbad ntdll!RtlpAllocateHeap+0x000000ed

779eb0cf ntdll!RtlpAllocateHeapInternal+0x0000022f

779eae8e ntdll!RtlAllocateHeap+0x0000003e

6f080269 MSVCR100!malloc+0x0000004b

6f08233b MSVCR100!operator new+0x0000001f

6b726c67 QtCore4!QImageData::create+0x000000fa

6b726b54 QtCore4!QImage::QImage+0x0000004e

6b7a0e21 QtCore4!png_get_text+0x00000436

6b79d7a8 QtCore4!QImageIOHandler::setFormat+0x000000de

6b79d457 QtCore4!QPixmapData::fromFile+0x000002bf

6b725eb4 QtCore4!QImageReader::read+0x000001e2

6d0ca585 kso!kpt::VariantImage::forceUpdateCacheImage+0x0000254e

6d0c5964 kso!kpt::Direct2DPaintEngineHelper::operator=+0x00000693

6d0c70d0 kso!kpt::RelativeRect::unclipped+0x00001146

6d0c8d0c kso!kpt::VariantImage::forceUpdateCacheImage+0x00000cd5

6d451d5c kso!BlipCacheMgr::BrushCache+0x0000049a

6d451e85 kso!BlipCacheMgr::GenerateBitmap+0x0000001d

6d453227 kso!BlipCacheMgr::GenCachedBitmap+0x00000083

6d29bb92 kso!drawing::PictureRenderLayer::render+0x000009b6

6d450fb1 kso!drawing::RenderTargetImpl::paint+0x00000090

6d29b528 kso!drawing::PictureRenderLayer::render+0x0000034c

6d2a2d83 kso!drawing::VisualRenderer::render+0x00000060

6d2b8970 kso!drawing::SingleVisualRenderer::drawNormal+0x000002b5

6d2b86a7 kso!drawing::SingleVisualRenderer::draw+0x000001e1

6d2b945e kso!drawing::SingleVisualRenderer::draw+0x00000046

6d3d0142 kso!drawing::ShapeVisual::paintEvent+0x0000044a

680a2b5c wpsmain!WpsShapeTreeVisual::getHittestSubVisuals+0x000068f1

6d0e36df kso!AbstractVisual::visualEvent+0x00000051

6d3cbe97 kso!drawing::ShapeVisual::visualEvent+0x0000018f

6d0eba90 kso!VisualPaintEvent::arriveVisual+0x0000004e

0:000> dt _DPH_BLOCK_INFORMATION cc780e18-0x20

verifier!_DPH_BLOCK_INFORMATION

+0x000 StartStamp : 0xc0c0c0c0

+0x004 Heap : 0xc0c0c0c0 Void

+0x008 RequestedSize : 0xc0c0c0c0

+0x00c ActualSize : 0xc0c0c0c0

+0x010 Internal : _DPH_BLOCK_INTERNAL_INFORMATION

+0x018 StackTrace : 0xc0c0c0c0 Void

+0x01c EndStamp : 0xc0c0c0c0

段中的最後一個堆條目通常是一個空閒塊。堆塊的狀態指示為空閒塊,堆塊宣告前一個塊的大小為00108,而當前塊的大小為00a30。前一塊報告其自身大小為0x20位元組,不匹配。位置為05f61000的堆塊的使用似乎是該堆塊的使用導致以下塊的元資料損壞的可能性。堆塊如下:

0:000> !heap -a 05f60000

Index Address Name Debugging options enabled

1: 05f60000

Segment at 05f60000 to 0605f000 (00001000 bytes committed)

Flags: 00000002

ForceFlags: 00000000

Granularity: 8 bytes

Segment Reserve: 00100000

Segment Commit: 00002000

DeCommit Block Thres: 00000200

DeCommit Total Thres: 00002000

Total Free Size: 00000146

Max。 Allocation Size: fffdefff

Lock Variable at: 05f60258

Next TagIndex: 0000

Maximum TagIndex: 0000

Tag Entries: 00000000

PsuedoTag Entries: 00000000

Virtual Alloc List: 05f6009c

Uncommitted ranges: 05f6008c

05f61000: 000fe000 (1040384 bytes)

FreeList[ 00 ] at 05f600c0: 05f605b8 。 05f605b8

05f605b0: 00108 。 00a30 [100] - free

Segment00 at 05f60000:

Flags: 00000000

Base: 05f60000

First Entry: 05f604a8

Last Entry: 0605f000

Total Pages: 000000ff

Total UnCommit: 000000fe

Largest UnCommit:00000000

UnCommitted Ranges: (1)

Heap entries for Segment00 in Heap 05f60000

address: psize 。 size flags state (requested size)

05f60000: 00000 。 004a8 [101] - busy (4a7)

05f604a8: 004a8 。 00108 [101] - busy (107) Internal

05f605b0: 00108 。 00a30 [100]

05f60fe0: 00a30 。 00020 [111] - busy (1d)

05f61000: 000fe000 - uncommitted bytes。

0:000> dd 05f60fe0

05f60fe0 a9b3c836 03007087 05f6008c 05f6008c

05f60ff0 05f60038 05f60038 05f61000 000fe000

05f61000 ???????? ???????? ???????? ????????

05f61010 ???????? ???????? ???????? ????????

05f61020 ???????? ???????? ???????? ????????

05f61030 ???????? ???????? ???????? ????????

05f61040 ???????? ???????? ???????? ????????

05f61050 ???????? ???????? ???????? ????????

0x03 披露時間表

該漏洞於2020年8月報告,披露時間表:

·

2020-08-04-將電子郵件傳送到公開提供的WPS的各種郵件列表(銷售和支援)。

·

2020-08-10-WPS團隊迴應該報告可以轉發給他們。

·

2020-08-11-要求進一步的資訊,例如向適當的渠道披露等。

·

2020-08-17-根據先前的要求與WPS團隊進行跟進。

·

2020-08-18-透過電子郵件提供技術報告和概念驗證(未加密)。

·

2020-08-25-WPS跟進報告進度。

·

2020-08-26-WPS更新說此問題已轉發給開發團隊。

·

2020-08-28-WPS傳送了一封電子郵件,指出該問題已在最新的下載版本11。2。0。9403中得到解決。

·

2020-08-28-針對提供的PoC測試了新版本,並確認問題已解決。

·

2020-08-28-向WPS團隊尋求諮詢或更改日誌更新。

·

2020-09-03-申請漏洞CVE。

·

2020-09-14-已分配CVE編號:CVE-2020-25291。

參考及來源:http://zeifan。my/security/rce/heap/2020/09/03/wps-rce-heap。html

TAG: 00000000c0c0c0c000002bQtCore4