BUUCTF Dig the way
文章目錄
- 題目類(lèi)型
- 查殼
- 拖進(jìn)ida
- 整體邏輯
- 文件讀取函數(shù)
- fseek函數(shù)
- ftell函數(shù)
- fread函數(shù)(補(bǔ)充)
- 分析三個(gè)func
- func0
- func1
- func2
- 解決方法分析
- 分析
題目類(lèi)型
這道題是一道棧溢出的題目,有點(diǎn)意思。
查殼
無(wú)殼
拖進(jìn)ida
int __cdecl main(int argc, const char **argv, const char **envp) {int result; // eax@2int v4; // ebx@6size_t v5; // eax@8int v6; // ebx@11int v7; // [sp+1Ch] [bp-48h]@6int v8; // [sp+30h] [bp-34h]@1signed int v9; // [sp+34h] [bp-30h]@1signed int v10; // [sp+38h] [bp-2Ch]@1signed int v11; // [sp+3Ch] [bp-28h]@1int v12; // [sp+40h] [bp-24h]@1int v13; // [sp+44h] [bp-20h]@1int (__cdecl *v14)(int, int, int); // [sp+48h] [bp-1Ch]@1int (__cdecl *v15)(int, int, int); // [sp+4Ch] [bp-18h]@1int (__cdecl *v16)(int, int, int); // [sp+50h] [bp-14h]@1__int32 v17; // [sp+54h] [bp-10h]@3__int32 v18; // [sp+58h] [bp-Ch]@3FILE *v19; // [sp+5Ch] [bp-8h]@1__main();v14 = func0;v15 = func1;v16 = func2;v8 = 0;v9 = 1;v10 = 2;v11 = 3;v12 = 3;v13 = 4;v19 = fopen("data", "rb");if ( v19 ){fseek(v19, 0, 2);v18 = ftell(v19);fseek(v19, 0, 0);v17 = ftell(v19);if ( v17 ){puts("something wrong");result = 0;}else{for ( i = 0; i < v18; ++i ){v4 = i;*((_BYTE *)&v7 + v4) = fgetc(v19);}v5 = strlen((const char *)&v7);if ( v5 <= v18 ){v18 = v11;i = 0;v17 = v13;while ( i <= 2 ){v6 = i + 1;*(&v8 + v6) = (*(&v14 + i))(&v8, v12, v13);v12 = ++i;v13 = i + 1;}if ( v11 ){result = -1;}else{get_key(v18, v17);system("PAUSE");result = 0;}}else{result = -1;}}}else{result = -1;}return result; }如何獲取到flag?
需要執(zhí)行g(shù)et_key函數(shù),如果要執(zhí)行,那么需要保證v11等于0。那么接下來(lái)我們來(lái)理理整體邏輯,
整體邏輯
v14 = func0;v15 = func1;v16 = func2;v14,v15,v16三個(gè)函數(shù)指針,分別指向func0,func1,func2,讀取data文件到v7,v7數(shù)組只有20個(gè)字節(jié)的大小,如果文件內(nèi)容超過(guò)20字節(jié),那么就會(huì)依次向后(也就是說(shuō)把v8,v9,v10等等)進(jìn)行覆蓋
while ( i <= 2 ){v6 = i + 1;*(&v8 + v6) = (*(&v14 + i))((int)&v8, v12, v13);v12 = ++i;v13 = i + 1;}v8的起始值是0,v6是1,所以這里func0(v14指向的函數(shù))的返回值是賦給v9;func1(v15指向的函數(shù))的的返回值是賦給v10;func2(v16指向的函數(shù))的的返回值是賦給v11(這里是不是找到關(guān)鍵點(diǎn)了???func2的返回值給v11,繼續(xù)往下觀察)
文件讀取函數(shù)
fseek函數(shù)
FILE *fp = fopen(model_path, "rb"); fseek(fp, 0, SEEK_END)用 法: int fseek(FILE *stream, long offset, int fromwhere);
描 述: 函數(shù)設(shè)置文件指針stream的位置。如果執(zhí)行成功,stream將指向以fromwhere為基準(zhǔn),偏移offset個(gè)字節(jié)的位置。如果執(zhí)行失敗(比如offset超過(guò)文件自身大小),則不改變stream指向的位置。
返回值: 成功,返回0,否則返回其他值。
參數(shù):
SEEK_SET: 文件開(kāi)頭
SEEK_CUR: 當(dāng)前位置
SEEK_END: 文件結(jié)尾
其中SEEK_SET,SEEK_CUR和SEEK_END和依次為0,1和2;
例子:
ftell函數(shù)
FILE *fp = fopen(model_path, "rb"); fseek(fp, 0, SEEK_END); //fp指針移到文件尾部 int model_len = ftell(fp);用 法: long ftell(FILE *fp);
描 述: 返回當(dāng)前文件指針位置。這個(gè)位置是當(dāng)前文件指針相對(duì)于文件開(kāi)頭的位移量。
返回值:返回文件指針的位置,若出錯(cuò)則返回-1L。
參數(shù):文件指針。
fread函數(shù)(補(bǔ)充)
fread(buffer,100,1,fp)用 法: size_t fread( void *buffer, size_t size, size_t count, FILE *stream ) ;
描 述: fread()用來(lái)從文件流中讀取數(shù)據(jù)。參數(shù)stream為已打開(kāi)的文件指針,參數(shù)buffer指向欲存放讀取進(jìn)來(lái)的數(shù)據(jù)空間,讀取的字節(jié)數(shù)以參數(shù)size * count來(lái)決定。
返回值: 返回實(shí)際讀取到的count數(shù)目,如果此值比參數(shù)count來(lái)得小,則代表可能讀到了文件尾了或者有錯(cuò)誤發(fā)生(前者幾率大),這時(shí)必須用feof()或ferror()來(lái)決定發(fā)生什么情況。
參數(shù):
分析三個(gè)func
func0
signed int __cdecl func0(int a1, int a2, int a3) {int v3; // ST0C_4@1v3 = *(_DWORD *)(4 * a2 + a1);*(_DWORD *)(a1 + 4 * a2) = *(_DWORD *)(4 * a3 + a1);*(_DWORD *)(a1 + 4 * a3) = v3;return 1; }func0一看就是利用一個(gè)temp把兩個(gè)東西交換一下。
func1
int __cdecl func1(int a1, int a2, int a3) {int v3; // eax@1v3 = (*(_DWORD *)(4 * a2 + a1) + *(_DWORD *)(4 * a3 + a1)) >> 31;return (v3 ^ (*(_DWORD *)(4 * a2 + a1) + *(_DWORD *)(4 * a3 + a1)))- v3- (((*(_DWORD *)(4 * a3 + a1) >> 31) ^ *(_DWORD *)(4 * a3 + a1))- (*(_DWORD *)(4 * a3 + a1) >> 31))- abs(*(_DWORD *)(4 * a2 + a1))+ 2; }func1的return值化簡(jiǎn)后為4a2+a1-abs(4a2+a1)+2(注意,這里我把右移31位直接看成0了,32位的數(shù)最高位為1被輸入的話很難遇到,直接忽略。)
4a2+a1-abs(4a2+a1)+2也就是y=x-|x|+2的函數(shù)圖像,如下:
這里返回值的分情況,有小于0,有大于0,有等于0
func2
int __cdecl func2(int a1, int a2, int a3) {int v3; // ecx@1v3 = (*(_DWORD *)(4 * a3 + a1) + *(_DWORD *)(4 * a2 + a1)) >> 31;return ((*(_DWORD *)(4 * a3 + a1) >> 31) ^ *(_DWORD *)(4 * a3 + a1))- (*(_DWORD *)(4 * a3 + a1) >> 31)- ((v3 ^ (*(_DWORD *)(4 * a3 + a1) + *(_DWORD *)(4 * a2 + a1)))- v3)+ abs(*(_DWORD *)(4 * a2 + a1))+ 2; }func2的return值化簡(jiǎn)后為-4a2-a1+abs((4a2 + a1))+2(注意,這里我把右移31位直接看成0了,32位的數(shù)最高位為1被輸入的話很難遇到,直接忽略。)
-4a2-a1+abs((4a2 + a1))+2也就是y=-x+|x|+2的函數(shù)圖像,如下:
也就是說(shuō)返回值恒大于0
解決方法分析
第一種方法,我們所畫(huà)出的函數(shù)恒為正,直接pass
第二種方法,如何改變呢?這里func0本來(lái)就是用來(lái)交換的,直接在調(diào)用func0函數(shù)時(shí)傳func1函數(shù)的函數(shù)指針v15和func2函數(shù)的函數(shù)指針v16進(jìn)行交換即可。交換之后,v16函數(shù)指針指向的函數(shù)就是func1,func1的返回值可正可負(fù)可為零,返回值賦給了v11,然后就可以對(duì)flag進(jìn)行打印輸出。
分析
signed int __cdecl func0(int a1, int a2, int a3) {int v3; // ST0C_4v3 = *(_DWORD *)(4 * a2 + a1);*(_DWORD *)(a1 + 4 * a2) = *(_DWORD *)(4 * a3 + a1);*(_DWORD *)(a1 + 4 * a3) = v3;return 1; } *(&v8 + v6) = (*(&v14 + i))((int)&v8, v12, v13);觀察func0,此時(shí)func0函數(shù)中參與運(yùn)算的起始地址是v8,偏移分別是v12(3)和v13(4),也就是說(shuō),此時(shí)func0交換的參數(shù)是v11和v12。要使參數(shù)變?yōu)関15和v16,就需要把偏移改為7和8,也就是把v12的值改為7,v13 的值改為8(其實(shí)顛倒過(guò)來(lái)也行)
v7有20個(gè)字節(jié),v8~v11是4個(gè)int,也就是4x4=16個(gè)字節(jié),于是data文件需要從第36個(gè)字節(jié)開(kāi)始,將v12和v13覆蓋為7和8。
此時(shí)僅僅只是交換了兩個(gè)函數(shù)指針,如何保證func1的返回值為0呢?
v15執(zhí)行func2函數(shù),v16執(zhí)行func1函數(shù),而在循環(huán)中v12和v13由i賦值,當(dāng)執(zhí)行v16(func1)函數(shù)時(shí),v12和v13的值分別為2和3
4a2+a1-abs(4a2+a1)+2,v10等于2,要使返回的值為0,則需v11為-1,于是同樣利用程序data讀文件的漏洞將v11覆蓋為-1
總結(jié)
以上是生活随笔為你收集整理的BUUCTF Dig the way的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: BUUCTF 特殊的BASE64
- 下一篇: [NPUCTF2020]芜湖(Base6