日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BUUCTF Dig the way

發布時間:2025/3/21 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BUUCTF Dig the way 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 題目類型
    • 查殼
    • 拖進ida
    • 整體邏輯
    • 文件讀取函數
      • fseek函數
      • ftell函數
      • fread函數(補充)
    • 分析三個func
      • func0
      • func1
      • func2
    • 解決方法分析
      • 分析

題目類型

這道題是一道棧溢出的題目,有點意思。

查殼


無殼

拖進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?
需要執行get_key函數,如果要執行,那么需要保證v11等于0。那么接下來我們來理理整體邏輯,

整體邏輯

v14 = func0;v15 = func1;v16 = func2;

v14,v15,v16三個函數指針,分別指向func0,func1,func2,讀取data文件到v7,v7數組只有20個字節的大小,如果文件內容超過20字節,那么就會依次向后(也就是說把v8,v9,v10等等)進行覆蓋

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指向的函數)的返回值是賦給v9;func1(v15指向的函數)的的返回值是賦給v10;func2(v16指向的函數)的的返回值是賦給v11(這里是不是找到關鍵點了???func2的返回值給v11,繼續往下觀察)

文件讀取函數

fseek函數

FILE *fp = fopen(model_path, "rb"); fseek(fp, 0, SEEK_END)

用 法: int fseek(FILE *stream, long offset, int fromwhere);
描 述: 函數設置文件指針stream的位置。如果執行成功,stream將指向以fromwhere為基準,偏移offset個字節的位置。如果執行失敗(比如offset超過文件自身大小),則不改變stream指向的位置。
返回值: 成功,返回0,否則返回其他值。
參數:

  • stream:文件指針;
  • offset:偏移量,整數表示正向偏移,負數表示負向偏移;
  • fromwhere:設定從文件的哪里開始偏移,可能取值為:SEEK_CUR,SEEK_END 或 SEEK_SET
    SEEK_SET: 文件開頭
    SEEK_CUR: 當前位置
    SEEK_END: 文件結尾
  • 其中SEEK_SET,SEEK_CUR和SEEK_END和依次為0,1和2;
    例子:

  • fseek(fp,50L,0);把fp指針移動到離文件開頭50字節處;
  • fseek(fp,50L,1);把fp指針移動到離文件當前位置50字節處;
  • fseek(fp,50L,2);把fp指針退回到離文件結尾50字節處。
  • ftell函數

    FILE *fp = fopen(model_path, "rb"); fseek(fp, 0, SEEK_END); //fp指針移到文件尾部 int model_len = ftell(fp);

    用 法: long ftell(FILE *fp);
    描 述: 返回當前文件指針位置。這個位置是當前文件指針相對于文件開頭的位移量。
    返回值:返回文件指針的位置,若出錯則返回-1L。
    參數:文件指針。

    fread函數(補充)

    fread(buffer,100,1,fp)

    用 法: size_t fread( void *buffer, size_t size, size_t count, FILE *stream ) ;
    描 述: fread()用來從文件流中讀取數據。參數stream為已打開的文件指針,參數buffer指向欲存放讀取進來的數據空間,讀取的字節數以參數size * count來決定。
    返回值: 返回實際讀取到的count數目,如果此值比參數count來得小,則代表可能讀到了文件尾了或者有錯誤發生(前者幾率大),這時必須用feof()或ferror()來決定發生什么情況。
    參數:

  • buffer :讀取的數據存放的內存的指針(可以是數組,也可以是新開辟的空間,buffer就是一個索引);
  • size : 每次讀取的字節數 ;
  • count :讀取次數 ;
  • strean:要讀取的文件的指針;
  • 分析三個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一看就是利用一個temp把兩個東西交換一下。

    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值化簡后為4a2+a1-abs(4a2+a1)+2(注意,這里我把右移31位直接看成0了,32位的數最高位為1被輸入的話很難遇到,直接忽略。)
    4a2+a1-abs(4a2+a1)+2也就是y=x-|x|+2的函數圖像,如下:

    這里返回值的分情況,有小于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值化簡后為-4a2-a1+abs((4a2 + a1))+2(注意,這里我把右移31位直接看成0了,32位的數最高位為1被輸入的話很難遇到,直接忽略。)
    -4a2-a1+abs((4a2 + a1))+2也就是y=-x+|x|+2的函數圖像,如下:

    也就是說返回值恒大于0

    解決方法分析

  • 使func2返回值為0
  • 使v16函數指針指向的函數改變(指向v15)
  • 第一種方法,我們所畫出的函數恒為正,直接pass
    第二種方法,如何改變呢?這里func0本來就是用來交換的,直接在調用func0函數時傳func1函數的函數指針v15和func2函數的函數指針v16進行交換即可。交換之后,v16函數指針指向的函數就是func1,func1的返回值可正可負可為零,返回值賦給了v11,然后就可以對flag進行打印輸出。

    分析

    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,此時func0函數中參與運算的起始地址是v8,偏移分別是v12(3)和v13(4),也就是說,此時func0交換的參數是v11和v12。要使參數變為v15和v16,就需要把偏移改為7和8,也就是把v12的值改為7,v13 的值改為8(其實顛倒過來也行)

    v7有20個字節,v8~v11是4個int,也就是4x4=16個字節,于是data文件需要從第36個字節開始,將v12和v13覆蓋為7和8。

    此時僅僅只是交換了兩個函數指針,如何保證func1的返回值為0呢?
    v15執行func2函數,v16執行func1函數,而在循環中v12和v13由i賦值,當執行v16(func1)函數時,v12和v13的值分別為2和3

    while ( i <= 2 ){v6 = i + 1;*(&v8 + v6) = (*(&v14 + i))((int)&v8, v12, v13);v12 = ++i;v13 = i + 1;}

    4a2+a1-abs(4a2+a1)+2,v10等于2,要使返回的值為0,則需v11為-1,于是同樣利用程序data讀文件的漏洞將v11覆蓋為-1

    flag: 8cda1bdb68a72a392a3968a71bdb8cda

    總結

    以上是生活随笔為你收集整理的BUUCTF Dig the way的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。