攻防世界reverse新手區writeup

第一題 re1.exe

 

0x01.運行程序

 

可以看到需要輸入正確的flag

 

那麼現在,我們需要判斷程序是多少位的,有沒有加殼

 

0x02.exeinfope查詳細信息

 

可以看到程序是32位的,是Microsoft Visual c++編譯的,並且沒有加殼

 

注:查殼工具還有PEID,EID,但是推薦EID或者exeinfope,因爲,PEID查殼的時候有時候不準確

 

那麼,我們可以用靜態分析神器 IDA 打開,進一步分析了

 

0x03.

 

然後,查找主函數main,可以看到右側的是反彙編的彙編代碼,這時候,我們可以直接分析彙編語言,但是,彙編語言看起來太多,費勁。這個時候就可以是有IDA是最強大的功能F5了,它能夠直接將彙編代碼生成C語言代碼,雖然和這個程序的源碼不完全一樣,但是邏輯關係是一樣的

 

F5查看僞代碼

 

這是整個main函數的運算邏輯

 

可以看到一個關鍵的字符串,print(aFlag),那麼證明這就是輸入正確flag,然後,會輸出aFlag證明你的flag正確,然後,繼續往上分析,可以看到v3的值,是由strcmp()決定的,比較v5和輸入的字符串,如果一樣就會進入後面的if判斷,所以,我們繼續往上分析,看看哪裏又涉及v5,可以看到開頭的_mm_storeu_si128(),對其進行分析發現它類似於memset(),將xmmword_413E34的值賦值給v5,所以,我們可以得到正確的flag應該在xmmword_413E34中,然後,我們雙擊413E34進行跟進

 

可以看到一堆十六進制的數

 

這時,我們使用IDA的另一個功能 R ,能夠將十進制的數轉換爲字符串。

 

這就是我們最後的flag了

 

注:這裏要跟大家普及一個知識了,及大端與小端

 

假設一個十六進制數0x12345678

 

大端的存儲方式是:12,34,56,78,然後讀取的時候也是從前往後讀

 

小端的存儲方式是:78,56,34,12,然後讀取的時候是從後往前讀取

 

所以,最後的flag應該是:DUTCTF{We1c0met0DUTCTF}

 

0x04.運行程序輸入正確的flag

DUTCTF{We1c0met0DUTCTF}

 

第二題 game.exe

 

0x01.運行程序

 

可以看到程序應該是輸入正確順序使八個圖案都變亮

 

0x02.查看詳細信息

 

我們直接使用神器IDA

 

0x04.IDA

 

F5查看僞代碼

 

然後,我們跟進Main函數

 

可以看到這是整個函數的運算邏輯

 

先是判斷是輸入的是否是1-8,然後進入後面的if判斷然後進行循環,這個時候應該就是程序的亮暗的顯示,然後,如果byte_532E28每一位都是1,那麼,就會進入sub_457AB4,然後我們猜測這裏應該就是最後的flag的地方。

 

然後我們跟進 sub_457AB4

 

注:這裏說明一下,如果IDA不能正確的獲得自定義函數的名字,那麼IDA會用sub__加上自定義函數的起始地址來定義函數的名字

 

這裏只截取了後面的部分,發現函數進行了兩次xor運算,xor的逆運算也是xor,那麼我們就可以根據這個運算來寫腳本得到最後的flag

 

這裏看到v2和v59這就證明了這是兩個數組的運算,所以我們應該將上面的字符串分成兩個數組,分別從v2和v59開始

 

0x05.寫EXP

 

這裏先是通過循環,將a和b數組的值進行xor運算,然後再將數組a的值與0x13xor運算

 

chr():是將十六進制轉換爲字符串

 

0x05.運行腳本

 

#coding:utf-8

b=[18, 64, 98, 5, 2, 4, 6, 3, 6, 48, 49, 65, 32, 12, 48, 65, 31, 78, 62, 32, 49, 32, 1, 57, 96, 3, 21, 9, 4, 62, 3, 5, 4, 1, 2, 3, 44, 65, 78, 32, 16, 97, 54, 16, 44, 52, 32, 64, 89, 45, 32, 65, 15, 34, 18, 16, 0]

 

a=[123, 32, 18, 98, 119, 108, 65, 41, 124, 80, 125, 38, 124, 111, 74, 49, 83, 108, 94, 108, 84, 6, 96, 83, 44, 121, 104, 110, 32, 95, 117, 101, 99, 123, 127, 119, 96, 48, 107, 71, 92, 29, 81, 107,

 

90, 85, 64, 12, 43, 76, 86, 13, 114, 1, 117, 126, 0]

 

c = ""

i = 0

while (i<56):

a[i]^=b[i]

a[i]^=19

c = c + chr(a[i])

I += 1

print c

 

 

得到最後的flag: zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}

 

 

第三題 Hello, CTF

 

0x01.運行程序

 

輸入正確的flag,纔會顯示正確

 

0x02.查殼

 

是32位的程序,並且是Microsoft Visual C++編譯,而且沒有加殼

 

0x03.IDA

 

照舊,依舊先從main開始分析,然後,對main函數進行F5查看僞代碼

 

首先,可以看到先是將字符串複製到v13的位置,

 

然後,後面對輸入進行了判斷,輸入的字符串不能大於17

 

接着,將字符串以十六進制輸出,然後,再將得到的十六進制字符添加到v10

 

最後,進行比較,看輸入的字符串是否和v10的字符串相等,如果相等,則得到真確的flag

 

0x04.將字符串轉換爲十六進制

16進制"437261636b4d654a757374466f7246756e"轉換爲字符串

 

#coding:utf-8

 

a = '437261636b4d654a757374466f7246756e'

c =''

for i in range(0,len(a),2):

s = '0x' + a[i]+a[i+1]

print s

c += chr(int(s,16))

print c

 

 

得到了最後的flag是:CrackMeJustForFun

 

 

第四題 open-source.c

 

1.打開源碼

打開源碼

 

 1 #include <stdio.h>

 2 #include <string.h>

 3

 4 int main(int argc, char *argv[]) {

 5     if (argc != 4) {

 6         printf("what?\n");

 7         exit(1);

 8     }

 9

10     unsigned int first = atoi(argv[1]);

11     if (first != 0xcafe) {

12         printf("you are wrong, sorry.\n");

13         exit(2);

14     }

15

16     unsigned int second = atoi(argv[2]);

17     if (second % 5 == 3 || second % 17 != 8) {

18         printf("ha, you won't get it!\n");

19         exit(3);

20     }

21

22     if (strcmp("h4cky0u", argv[3])) {

23         printf("so close, dude!\n");

24         exit(4);

25     }

26

27     printf("Brr wrrr grr\n");

28

29     unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;

30

31     printf("Get your key: ");

32     printf("%x\n", hash);

33    

34     return 0;

35 }

 

 

2. 分析

很明顯,第29行計算flag,第32行代碼輸出十六進制形式。第29行代碼就是利用argv[1]~argv[3]的數據進行計算。

 

2.1 argv[1]

 

    if (first != 0xcafe) {

        printf("you are wrong, sorry.\n");

        exit(2);

    }

不等於0xcafe就退出,那first=0xcafe

 

2.2 argv[2]

 

    if (second % 5 == 3 || second % 17 != 8) {

        printf("ha, you won't get it!\n");

        exit(3);

    }

滿足if條件就退出,我想到第一個不滿足的數就是25,second = 25

 

2.3 argv[3]

 

    if (strcmp("h4cky0u", argv[3])) {

        printf("so close, dude!\n");

        exit(4);

    }

相等strcmp返回0,退出if條件,那argv[3]=“h4cky0u”

 

3.get flag!

綜上,寫出解flag代碼

 

 

#include <stdio.h>

#include <string.h>

 

int main(int argc, char* argv[]) {

 

    int first = 0xcafe;

    int second = 25;

    argv[3] = "h4cky0u";

 

    printf("Brr wrrr grr\n");

 

    unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;

 

    printf("Get your key: ");

    printf("%x\n", hash);

 

    system("PAUSE");

    return 0;

}

 

運行得到flag:

c0ffee

 

 

 第五題 simple-unpack

 

拿到文件先查殼,發現是upx的殼,elf文件

 

 

 直接放到kali裏面構造命令脫殼

 

Upx -d simple-unpack

 

 脫殼後的文件丟進IDA,明文flag

 

flag{Upx_1s_n0t_a_d3liv3r_c0mp4ny}

 

 

第六題 logmein

 

然後進入主函數,

 

 經過分析,可以得出:輸入的字符要等於  經過處理的v7和v8的異或。v8很明顯,但是v7是怎麼回事呢,新手沒有遇到過,所以上網查看資料,LL是長長整型,v7要轉換爲16進制然後在轉換爲字符串,而且字符是小端序,所以把得到的字符翻轉然後和v8的每一位進行異或。貼出腳本:

 

a = 'harambe'

b = ':\"AL_RT^L*.?+6/46'

print(b)

tmp = ''

for i in range(len(b)):

    c = ord(a[i % 7]) ^ ord(b[i])

    tmp += chr(c)

    print(tmp)

 

運行得flag:

RC3-2016-XORISGUD

 

第七題 insanity

 

拿到的同樣是elf文件,無殼,直接放到IDA,定位主函數

 

邏輯簡單,沒啥好說的,直接跟蹤strs,得到flag

 

9447{This_is_a_flag}

 

 

第八題 no-strings-attached

 

[分析過程]

 

0x01.查殼和查看程序的詳細信息

 

說明程序是ELF文件,32位

 

0x02.使用靜態分析工具IDA進行分析

 

然後對main函數使用F5查看僞代碼

 

然後,對每個函數進行跟進,最後發現authenricate(),符合獲得flag的函數,對其進行跟進

 

然後我們發現一個特殊的函數decrypt,根據字面的意思是加密,那麼我們可以大概的猜測是一個對dword_8048A90所對應的字符串進行加密,

 

加密得到的就應該是我們需要的flag,後面的判斷應該就是將字符串輸出。

 

這裏我們有兩種思維方式:

 

第一種就是跟進decrypt然後分析它的運算邏輯,然後,自己寫腳本,得到最後的flag

 

第二種就涉及逆向的另一種調試方式,及動態調試,這裏我就用動態調試了,之前的一直是靜態調試

 

0x03.GDB動態調試

 

gdb ./no_strings_attached 將文件加載到GDB中

 

既然是動態調試,那麼如果讓它一直不停,那我不就相當於運行了嘛,所以,我們就需要下斷點,斷點就是讓程序運行到斷點處就停止

 

之前通過IDA,我們知道關鍵函數是decrypt,所以我們把斷點設置在decrypt處,b在GDB中就是下斷點的意思,及在decrypt處下斷點

 

r就是運行的意思,這裏運行到了我們之前下的斷點處,停止。

 

我們要的是經過decrypt函數,生成的字符串,所以我們這裏就需要運行一步,GDB中用n來表示運行一步

 

然後我們就需要去查看內存了,去查找最後生成的字符串

 

通過IDA生成的彙編指令,我們可以看出進過decrypt函數後,生成的字符串保存在EAX寄存器中,所以,我們在GDB就去查看eax寄存器的值

 

x:就是用來查看內存中數值的,後面的200代表查看多少個,wx代表是以word字節查看看,$eax代表的eax寄存器中的值

 

在這裏我們看到0x00000000,這就證明這個字符串結束了,因爲,在C中,代表字符串結尾的就是"\0",那麼前面的就是經過decrypt函數生成的falg

 

那我們就需要將這些轉換爲字符串的形式

 

0x04.Write EXP

 

首先將寄存器中的值提取出來,然後利用Python的decode函數,通過"hex"的方式轉化爲字符串,然後輸出

 

0x05.運行腳本

 

得到最後的flag: 9447{you_are_an_international_mystery}

 

 

第九題 csaw2013reversing2

 

[分析過程]

 

0x01.查殼和查看程序的詳細信息

 

程序是32位的並且沒有加殼

 

0x02.運行程序

 

發現提示Flag,但是亂碼了

 

載入IDA,靜態分析

 

0x03.IDA

 

依舊從main函數開始分析,F5查看僞代碼

 

可以看到有個關鍵函數, 意思是如果在動態調試器中就進入判斷運行,如果沒有直接彈窗,顯示亂碼的值

 

在這裏我們看到了一個int 3中斷,所以,我們直接OD動態調試,到達中斷的位置,應該就能得到正確的flag了

 

這裏我們對比上圖的IDA,可以看出mov edx,[ebp+lpMem]對應的彙編指令地址,單步運行F8

 

執行了mov指令,接下來調用call,F8繼續執行,執行完,edx存的就是flag的地址

 

最後的flag是:flag{reversing_is_not_that_hard!}

 

還有另外一種做法ida修改:

 

• sub_40100 爲解密函數,必須經過 ,所以修改 jz short loc_4010B9 爲  jmp short loc_401096.

• loc_4010B9輸 出flag的函數,解密完應跳轉到loc_4010B9

•具體修改步驟:

•修改int 3爲 NOP.

•修改 jmp short loc_4010EF 爲 jmp short loc_4010B9.

•修改 jz short loc_4010B9 爲 jmp short loc_401096.

 

第十題 getit

 

ida64靜態分析,按F5生成pseudocode

 

int __cdecl main(int argc, const char **argv, const char **envp)

{

  char v3; // al

  __int64 v5; // [rsp+0h] [rbp-40h]

  int i; // [rsp+4h] [rbp-3Ch]

  FILE *stream; // [rsp+8h] [rbp-38h]

  char filename[8]; // [rsp+10h] [rbp-30h]

  unsigned __int64 v9; // [rsp+28h] [rbp-18h]

 

  v9 = __readfsqword(0x28u);

  LODWORD(v5) = 0;

  while ( (signed int)v5 < strlen(s) )

  {

    if ( v5 & 1 )

      v3 = 1;

    else

      v3 = -1;

    *(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;

    LODWORD(v5) = v5 + 1;

  }

  strcpy(filename, "/tmp/flag.txt");

  stream = fopen(filename, "w");

  fprintf(stream, "%s\n", u, v5);

  for ( i = 0; i < strlen(&t); ++i )

  {

    fseek(stream, p[i], 0);

    fputc(*(&t + p[i]), stream);

    fseek(stream, 0LL, 0);

    fprintf(stream, "%s\n", u);

  }

  fclose(stream);

  remove(filename);

  return 0;

}

下面是重點代碼

 

LODWORD(v5) = 0;

  while ( (signed int)v5 < strlen(s) )

  {

    if ( v5 & 1 )

      v3 = 1;

    else

      v3 = -1;

    *(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;

    LODWORD(v5) = v5 + 1;

  }

可以看出將s中的值賦給了t,然後最後是將t寫入文件flag.txt中,我們看一下s和t分別是什麼

 

現在我們可以編寫python腳本來解出flag

 

v5 = 0

s = 'c61b68366edeb7bdce3c6820314b7498'

t = ['S','h','a','r','i','f','C','T','F','{','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','?','}']

v3 = 0

l = len(s)

while(v5 < l):

    if( v5 & 1 ):

        v3 = 1

    else:

        v3 = -1

    t[10+v5] = chr(ord(s[v5])+v3)

    v5 += 1

c = ''

for x in t:

    c+=x

print(c)

 

輸出爲

SharifCTF{b70c59275fcfa8aebf2d5911223c6589}

 

 

 

第十一題 python-trade.py

 

是一個pyc文件,用python反編譯工具反編譯後可以看到代碼https://tool.lu/pyc/

 

import base64

 

def encode(message):

    s = ''

    for i in message:

        x = ord(i) ^ 32

        x = x + 16

        s += chr(x)

   

    return base64.b64encode(s)

 

correct = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt'

flag = ''

print 'Input flag:'

flag = raw_input()

if encode(flag) == correct:

    print 'correct'

else:

    print 'wrong'

編寫相應的腳本

 

#coding:utf-8

 

import base64

 

buf = base64.b64decode('XlNkVmtUI1MgXWBZXCFeKY+AaXNt')

 

flag = ''

 

for i in buf:

    i = ord(i)-16

    i ^= 32

    flag += chr(i)

 

print flag

 

輸出flag:

nctf{d3c0mpil1n9_PyC}

 

第十二題 maze

 

0x01.查殼和詳細信息

 

可以看到程序是ELF文件,64位

 

0x02.IDA

 

對main函數使用F5,查看僞代碼

 

從這裏可以看出先是進行判斷,如果滿足則進入判斷,開頭必須是以nctf{開頭的

 

然後往下分析

 

從這裏可以看出進行了四個判斷,然後,進入四個函數()

 

這裏就涉及逆向的一個有意思的問題那就是迷宮問題:

 

迷宮問題可以參考:

 

https://ctf-wiki.github.io/ctf-wiki/reverse/maze/maze/

 

我們輸入的應該是'.','0','o','O',並以此來確定上下左右移動

 

從上往下以此追蹤,可以發現這些函數會跳到lable15的位置,然後,對lable15分析,發現特殊的字符串

 

然後,猜測可能是一個8*8的迷宮

 

根據迷宮最後得到的flag:

nctf{o0oo00O000oooo..OO}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章