社團的CTF逆向題WriteUp


 

最近社團弄了CTF比賽,然后我就幫忙寫了逆向的題目,這里寫一下WriteUp,題目和源碼在附件中給出

 

一個簡單的逆向:one_jmp_to_flag.exe

這題算是簽到題,直接OD智能搜索就完事了,flag{Welcome_to_GeekFZCTF}

 

一個簡單的對比:check.exe

這一道也算是送分題,可以用IDA直接分析可以看出來,這里就是讀取輸入的字符串,然后逐個進行對比,這里可以直接用IDA轉化為char型就可以看出。flag{sAdf_fDfkl_Fdf}

 

 

 猜猜那個是flag:whichisflag.exe

 首先加載進IDA,先看一下基本邏輯,輸入的flag首先要小於25,然后進行判斷進行判斷,如果flag[5]=Y,且flag[8] = flag[11],flag[16] = flag[18],上面的都實現了就執行which_is_flag這個函數,我們跟進去看一下。 

 

 可以看到這是通過flag[5]進行switch跳轉,然后我們之前有個判斷條件flag[5]=Y,所以這里的跳轉就是到89處

 

 但是我們可以看到,這個flag的長度是大於25的,然后仔細觀察一下flag里的內容,可以看出是base64編碼過的,我們找個網站進行解碼就可以,這里的flag是用python隨機出來的,代碼也在附件里。flag{YkEj_djkf3_jEj_eUn}

 

 

 簡單的反調試:fts.exe

 其實這道題目就只是用OD調試時會遇到一點反調試,如果用IDA直接分析邏輯就可以了,這里首先用IDA分析先。

 

可以看到一個可疑的循環並printf出來字符 ,我們看到邏輯就是用flag_2這個數組的數據與0x11進行異或,我們手動計算一下得出字符串(這里注意的是0x76707D77是小端序,我們要從77開始異或):flag{

 

 然后我們可以繼續往下面找關鍵循環,函數CheckDebug、fts_Rdtsc、the_end里都有循環輸出,然后都解密出來就是:congratulations_for_you}

 

 

所以flag就是:flag{congratulations_for_you}

 

然后用OD動態調試一下,一開始就是先進行進程的檢索,判斷是否存在IDA或者OD,然后我們可以通過je直接跳過進行判斷的地方。

 

 跳過第一個反調試的地方后就得看一下我們的main函數入口在哪里(為什么不先找main函數,因為你不跳過第一個反調試即使找到main函數入口也沒用,嘗試一下就發現跳過了第一個反調試就很容易去到main函數了),我們這里先智能搜索一下可以看到一些字符串,你可以每個都進去看一下,然后發現main函數就在走過八十一難哪里(用IDA可以直接找到)

 

 

 找到main函數后下斷點,然后F9跳過去就可以單步跟蹤了,我們可以發現會自己打印出了第一個解密循環

 

 

 下面繼續跟會發現有用到FindWindow函數去查找有沒有IDA和OD,然后我們只要不讓下面兩個je進行跳轉就可以,就會再打印出一段flag出來

 

 跟進去函數里面,可以發現用了ZwQueryInformationProcess進行檢測調試端口的,我們不讓它跳轉就會打印出字符串來

 

 

 再進入下一個函數中,在里面可以看到rdtsc命令,這個用於把時間保存的指令,這里是基於時間的反調試,我們可以直接在下面的popad下斷點然后F9直接跳過,然后就找到了解密循環。

 

 

 最后進入函數,我們可以看到有一個get函數並且下面的跳轉是關鍵,可以先隨機輸入一些東西,然后可以看到下面的跳轉跳過了我們的printf函數,所以我們讓它不跳轉,然后就跑出最后的flag出來。

 

 

 

 簡單的算法:suafa.exe

這是一個算法,然后這里要道個歉是出題不夠嚴謹,在跑flag的時候會出現多解的情況。正向的核心算法是輸入的字符串減去key之后對4求余,然后在key1到key4之間選擇字符,最后與我們給定的字符進行對比

 1     char key1[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 2     char key2[65] = "+/abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 3     char key3[65] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/";
 4     char key4[65] = "0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 5 
 6     char key[27] = "QASWZXDECVFRBNGTHYJUMKIOLP";
 7     int len = strlen(flag);
 8     for(int i=0; i<len; i++)
 9     {
10         if(int(flag[i]) <= 126 && int(flag[i]) >= 33)
11         {
12             if(flag[i]-key[i] > 0)
13             {
14                 int the_key = (flag[i]-key[i]) % 4;
15                 switch (the_key)
16                 {
17                     case 0:
18                         flag[i] = key1[flag[i]-key[i]];
19                         continue;
20                     case 1:
21                         flag[i] = key2[flag[i]-key[i]];
22                         continue;
23                     case 2:
24                         flag[i] = key3[flag[i]-key[i]];
25                         continue;
26                     case 3:
27                         flag[i] = key4[flag[i]-key[i]];
28                         continue;
29                     default:
30                         continue;
31                 }                
32             }
33             else
34             {
35                 printf("flag is wrong");
36                 ExitProcess(0);
37             }
38         }
39     }

 

 然后在逆向的時候我們用IDA打開基本都能編譯出來,然后有一點小問題就是v5 = v4 - v1[key-flag]這里,反編譯后與原來的代碼int the_key = (flag[i]-key[i])有點差別,不過如果看匯編的代碼可以更好的理解

 

 其中sub eax, flag就是我們的v4 - v1[key-flag],而v4=flag[i],所以理論上flag = v1[key-flag],而我們匯編中flag的值為byte ptr [ebx+edx],這里的ebx可以從前面查看就是key,而edx就相當於索引。不懂得可以自己仔細看一下。

 

 

然后分析完就可以寫個腳本自己跑出來:flag{this_is_a_easy_suanfa}

 1 #include "stdio.h"
 2 #include "Windows.h"
 3 
 4 int main()
 5 {
 6     char key1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 7     char key2[] = "+/abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 8     char key3[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/";
 9     char key4[] = "0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
10 
11     char key[] = "QASWZXDECVFRBNGTHYJUMKIOLP";
12 
13     char flag_1[] = "tfoQ5ckkwhX51HYpxAjkMQYTAp5";
14 
15     int yushu = 0;
16 
17     for(int m=0; m<=26; m++)
18     {
19         for(int n=33; n<=126; n++)
20         {
21             yushu = n - int(key[m]);
22             yushu %= 4;
23             switch (yushu)
24             {
25                 case 0:
26                     if(key1[n - int(key[m])] == flag_1[m])
27                     {
28                         printf("%c", n);
29                     }
30                     continue;
31                 case 1:
32                     if(key2[n - int(key[m])] == flag_1[m])
33                     {
34                         printf("%c", n);
35                     }
36                     continue;
37                 case 2:
38                     if(key3[n - int(key[m])] == flag_1[m])
39                     {
40                         printf("%c", n);
41                     }
42                     continue;
43                 case 3:
44                     if(key4[n - int(key[m])] == flag_1[m])
45                     {
46                         printf("%c", n);
47                     }
48                     continue;
49                 default:
50                     continue;
51             }            
52         }
53         printf("\n");
54     }
55 }

 

因為存在多解,所以跑出來得結果是這樣子的

 

 附件:題目、題目源碼和腳本:https://github.com/QKSword/CTF-GeekFZ

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM