3.5.6 字典對象處理流程
關鍵的函數為forall在處理dict的過程,我們在第三個語句塊首部分下斷點<`EPSIMP32!RegisterPercentCallback+0x4664`>,觀察斷點,入棧了3個參數,調用一函數,那么我們需要思考下這三個參數是什么,通過查詢<<PLRM2.PDF>>,我們得到關鍵性的資料,[`圖2`]上面說如果第一個操作是字典類型,那么它會入棧一個`key`和一個`數據`,這里類似哈希表的形式,通過一個key可以迅速得到對應的數值;為什么`圖1`我寫`pNext`,通過`圖3`我們可以判斷其內容是否為null,則`lea eax, [ebp-14]`eax取出來的是個指針,所以這里這個名字主要是方便識別其為指針所定。
圖1
圖2
圖3
3.5.7 對象結構解析
關于第一個CALL所做的事情,大致可以分為3個方面,給PNEXT附值,給KEY賦值,給VALUE賦值,這里說的簡單,里面的功能CALL需要自己手動調試下做好記錄更加方便理解是如何賦值的過程,這里直接說下是如何得到的這些數值,首先看下`ECX`所對應的內容`圖1`所示,其中`+0X8`偏移的位置是循環遍歷的次數,相當于一個`INDEX`也可以說是個`界限值`,里面的CALL循環條件的范圍就是 `400`, 其中`+0XC`的位置`8`表示`KEY`的個數,為什么,如`圖2`所示,那么關于`+0X0`偏移的位置則是個`基址`的意思,所有的結果都是通過[基址+偏移*X]的形式得到相應的數據,`圖3`表示第一個`KEY1`中的內容,同時`2B4`則為下標,通過基址+下標*4即可獲取第一個KEY1對應內容的地址,這0x28個字節就是個結構體,大致內容如下所示,正好可以跟我們的數據一一對應,不過這里還是需要`多跟幾遍`才會更加明白`結構`的情況,當跑完第一個CALL之后的內存情況如圖4所示。
圖1
圖2
圖3
struct { dword * pNext; //指向下個結構體 dword dwIndex; //下標 ps_obj key; //key ps_obj value; //數據 }kv_pair_element; struct PostScript object { dword type;//類型 dword attr; //屬性 dword value1; //數據 dword value2; //數據 }ps_obj; |
圖4
00030000[類型] 00000000[屬性] 000001ff[數據] 04f32ea4[數據] 00000300[類型] 00000000[屬性] 04f69db0[數據] 04f32ea4[數據] 04f8fd98[pNext] |
3.5.8 釋放空間函數分析
第二call,第三call,為同一call,在對key與value進行操作,copy到其他位置,之后的`PROC`則為重點call,此call則執行`forall`的所有的操作,我們只需要在copy函數的位置下斷點即可得到出發漏洞的位置<copy:`EPSIMP32!RegisterPercentCallback+0x15582`>,然后我們單步跟蹤即可,這里可以先用ida觀察下copy的大致流程,然后通過動態的方式更好的去跟蹤我們想要的數據,由于copy的函數塊比較大,動態調試起來也不是特別容易,我這里跟的時候是每個call都跟進去了,很費時間,但肯定可以找到想要的函數數據的,這里我直接給下偏移<`EPSIMP32!RegisterPercentCallback+0x12e8e`>,如`圖1`所示為釋放過程的函數<`EPSIMP32+0x1a0e8`>,這個釋放的過程可以仔細觀察下,還記得開始的時候的this指針所指向的內容嗎?[[this]],記得第一個call的時候,我們獲取KEY1-KEY8都是存在以[[this]]為基址,然后加上一個偏移得到這些數據,第一次的循環delete也是從[[this]]為基址開始遍歷,判斷是否為null,不是空則delete,那么這正片空間都被delete了,這個的范圍也是我們剛剛進入第一個call的時候的一個下標`400`,循環結束之后,delete this,并且清空`+0x0`, `+0x8`,就是清空key的個數。
圖1
圖2(釋放:dict1 copy dict2—>釋放dict2過程)
3.5.9 copy字典對象
之后會進行拷貝的操作,要把dict1–>copy到dict2中,則會new一段空間,而這段空間剛剛好為之前所釋放的[[this]],這里new的是`0x1000`大小,并且通過`memset`清空剛剛申請的空間中的內容`圖1,2`所示,之后會繼續new0x28空間大小,為什么?因為`dect1–>copy–>dect2`中,diect1只有`1個key`,則在0x1000中所填充的數據也就是1個4字節地址,所以需要new 0x28大小來保存數據圖3所示,copy完數據`圖4`所示。
圖1
圖2(新new空間與釋放的dict2一致)
圖3
圖4(new0x28大小空間:內容pNext,index, 類型,屬性,數據,數據,類型,屬性,數據,數據)
3.5.10 填充構造數據
之后則為putinterval操作的過程,這個就會填充我們的pNext指針所指向的數據空間,斷點位置<`EPSIMP32!RegisterPercentCallback+0x160da`>,然后會進行`memcpy`的操作,把我們數據正好填充到我們之前的pNext所指向的空間中,這樣會whlile循環會再次跑一邊,而這次的 key,vale則是我們自己所構造的指定數據,結構如下所示。
圖1
00000000[pNext] 000003ff[index] 00000003[類型] 00000000[屬性] 00000000[key.valu1] 44444444[key.value2] 00000500[類型] 00000000[屬性] 00000000[value.1] 0462e0b0[value.2]//這里是原來數據,并非復制過來的數據 |