文章目錄
- status: 寫完了新手模式,後面慢慢接觸更深的吧。(20190819)
reverse-新手模式:
re1:
IDA中F5
查看反彙編之後的C代碼:
這裏將輸入的字符串放在變量v11中,然後與v7對比,v7和v8是連續的棧中變量,先後存放flag的一部分,找到給變量賦值的地方:
然後用python處理字符串輸出:
game:
載入ida分析,找到得出flag的地方,分析一下,程序預設兩個一樣長度(56)的字符串,通過異或的方法恢復出flag,關鍵部分如下:
依據這個,寫出python腳本來得到flag:
#! usr/bin/python3
str0 = [0x12,0x40,0x62,0x5,0x2,4,6,3,6,0x30,0x31,0x41,0x20,0x0C,0x30,0x41,0x1F,0x4E,0x3E,
0x20,0x31,0x20,1,0x39,0x60,3,0x15,9,4,0x3E,3,5,4,1,2,3,0x2C, 0x41, 0x4E, 0x20,0x10,
0x61,0x36, 0x10, 0x2C,0x34, 0x20, 0x40, 0x59, 0x2D, 0x20, 0x41, 0x0F, 0x22, 0x12,
0x10]
str1 = [0x7b,0x20,0x12,0x62,0x77,0x6c,0x41,0x29,0x7c,0x50,0x7D,0x26,0x7c,0x6f,0x4a,0x31,0x53,
0x6c,0x5e,0x6c,0x54,6,0x60,0x53,0x2c,0x79,0x68,0x6e,0x20,0x5f,0x75,0x65,0x63,0x7b,
0x7f,0x77,0x60,0x30,0x6b,0x47,0x5c,0x1d,0x51,0x6b,0x5a,0x55,0x40,0x0c,0x2b,0x4c,
0x56,0x0d,0x72,1,0x75,0x7e]
for i in range(56):
str1[i] ^= str0[i]
str1[i] ^= 0x13
for i in str1:
print (chr(i),end='')
執行得到flag:
Hello, CTF
直接拖入IDA分析,經過相關分析並註釋如下:
查看v13的具體內容:
又是16進制轉ASCII碼:
open-source
題目直接給了源碼,不難,依次滿足給定的命令行參數條件即可,不過第一個參數好像構造不出來,因爲0xcafe已經超出ascii的表示範圍了,所以就直接改源碼,將最後計算hash的那句代碼改了,構造其他3個就行了。
修改源代碼:註釋對第一個參數的判斷,修改最後hash的計算
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc, char *argv[]) {
if (argc != 4) {
printf ("what?\n");
exit (1);
}
/*unsigned int first = atoi (argv[1]);
printf ("%x\n", first);
if (first != 0xcafe) {
printf ("you are wrong, sorry.\n");
exit (2);
}*/
unsigned int second = atoi (argv[2]);
/*printf ("%d\n", first);*/
if (second % 5 == 3 || second % 17 != 8) {
printf ("ha, you won't get it!\n");
exit (3);
}
if (strcmp ("h4cky0u", argv[3])) {
printf ("so close, dude!\n");
exit (4);
}
printf ("Brr wrrr grr\n");
unsigned int hash = 0xcafe * 31337 + (second % 17) * 11 + strlen (argv[3]) - 1615810207;
printf ("Get your key: ");
printf ("%x\n", hash);
return 0;
}
構造參數得到flag:
simple-unpack
ELF二進制程序,題目提示加殼,最近遇到的幾個逆向的題也是加殼的,upx加殼和aspack加殼,在網上找的一些脫殼的軟件大部分不能成功脫殼,應該是要學會自己手動脫殼吧,想起上學期老師說的脫殼技術,真的是頭大啊,很菜,都沒做出來,只能拿這個來攢攢信心了。
使用010 Editor二進制編輯器查看,文件類型:
有顯示upx!
說明是被upx加殼了,之前說的遇到的被upx
加殼的軟件這樣打開來看的話會是upx0, upx1
,如果是Windows
的PE
程序,可以使用PEid
來看,這裏的這個是ELF程序,是Unix
上的二進制可執行程序,以後應該會遇到更多的吧。
所以我們就是用upx
來脫殼,upx
可以到SourceForge
上去下,是一個GitHub上的項目,一直在不斷的更新。下載解壓之後,進入到exe
文件所在目錄打開命令行,就可以脫殼了,upx
的具體命令用法可以查看下載的readme
文件,都說的很清楚。
如下圖:
這裏我是把upx.exe
的路徑設爲環境變量了,所以可以直接使用。
然後使用ida64位打開就可以直接看到flag了:
RC3 CTF 2016
好了,這回這個沒有加殼了,是一個64位的ELF
文件,拖入IDA
分析,F5
查看僞代碼:
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
size_t v3; // rsi@1
int i; // [sp+3Ch] [bp-54h]@3
char s[36]; // [sp+40h] [bp-50h]@1
int v6; // [sp+64h] [bp-2Ch]@1
__int64 v7; // [sp+68h] [bp-28h]@1//var_28
char v8[8]; // [sp+70h] [bp-20h]@1
int v9; // [sp+8Ch] [bp-4h]@1
v9 = 0;
strcpy(v8, ":\"AL_RT^L*.?+6/46");
v7 = 28537194573619560LL;//注意這裏
v6 = 7;
printf("Welcome to the RC3 secure password guesser.\n", a2, a3);
printf("To continue, you must enter the correct password.\n");
printf("Enter your guess: ");
__isoc99_scanf("%32s", s);
v3 = strlen(s);
if ( v3 < strlen(v8) )
wrong();
for ( i = 0; i < strlen(s); ++i )
{
if ( i >= strlen(v8) )
wrong();
if ( s[i] != (char)(*((_BYTE *)&v7 + i % v6) ^ v8[i]) )//關鍵點
wrong();
}
success();
}
提示輸入flag
到s
變量中去,然後在用一個for
循環判斷是否正確,比較簡單,flag
就是v7
數組和v8
數組的字符異或,但是往上看v7
顯示是一個長整型的值,跟蹤過去進入彙編代碼:
上圖紅色標記處把數據段的qword_40080
數據傳入rax
,rax
再賦值給本地變量var_28
,而由上面的僞代碼,我們知道這個就是v7
。我們再繼續到數據段中去看:
使用R
鍵將數據轉換成字符形式,得到:
這裏我就跟着去寫python
代碼來恢復flag
了,但是恢復出來提交不對。
顯然,結果也沒有什麼意義,雖然有些題的flag
可能沒有什麼意義。
後來(如果不去看提示的話估計我會卡很久),比對了v8
的賦值,才知道這是因爲小端存放的原因,低字節的數據存放在高地址上面,如下面的兩張圖,即就是v8
的賦值和數據存放的方式:
於是v7
的值也需要把順序倒過來,即:
這樣flag
就出來了,發現cygwin
在Windows
下用着是真的方便啊,現在電腦上面可以使用的命令行就有power shell, cmd, git bash,cygwin
。
insanity
瘋狂的;照例打開下載的文件,010Editor
查看是ELF
文件,所以使用IDA
打開,發現F5
不管用了,看了一下main
函數,也沒有什麼邏輯,陷入尷尬境地。查看Strings
窗口,發現一個類似flag
的flag
:
這一看就不像真正的flag
,況且還有其他字符串的信息。
結果真香,後來找思路的時候回到題目提示中去看,菜雞覺得前面的太難了,來個簡單的,結果就是把上面的flag
提交就可以了。套路,都是套路!
no_strings_attached
題目提示運行程序就能拿flag,elf程序,kali中運行,顯示段錯誤:
使用ida打開,查看導出表,有一個decrypt,解密的,再使用gdb調試設置斷點在這兒:
然後使用r
命令運行程序,就會在斷點處停下,然後單步步過n
,使用info reg
查看寄存器的值,這時解密的結果應該在寄存器eax中了:
eflags
寄存器爲0x282,'x/282 $eax’查看eax的內容(這裏查看文檔的,還不太懂,以後懂了回來再寫):
紅框中的數據即爲flag
:
csaw2013reversing2
exe文件,使用ida打開,提示有調試信息,猜想後續可能和調試有關:
查看主函數的graph圖:
在這之前已經試運行過這個程序,彈出了一個對話框,標題爲flag,可是顯示的是亂碼:
仔細分析之後,程序會先使用IsDebuggerPresent
函數判斷程序是否在調試器中運行,是的話調到解密函數,否則彈框輸出亂碼的flag,仔細來看是在調試器中運行後的這一部分:
有一個int 3
中斷,然後[ebp+lpMem]
是存放亂碼flag的地址,賦值給edx
作爲後續處理,接着調用函數sub_401000
,一開始我不知道這是解密函數,進去看了之後才知道的,sub_401000
函數分析出來是將亂碼的flag
4個字節爲一組與0xAABBCCDD
異或得到可識別的flag,本來想寫一個腳本來解出flag的,但是想試試調試的過程。
使用OD打開exe文件,因爲程序本來就有中斷,所以運行幾次之後就找到了將要調用解密函數的位置:
如上圖,地址000B109E
處就是調用解密函數的指令,在這裏下一個斷點,單步步過之後調到下一條指令,此時flag的地址是存放在edx中的,查看edx的值,爲02D005B8
:
到內存中去找,就可以看到flag了:
maze
注:學會了一個單詞,maze-迷宮。
一個月前看着很難,現在看着就很簡單了,函數的功能分析起來也容易得多。(2019.08.19)
下面是ida中分析的結果:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
signed __int64 i; // rbx
signed int a_ch; // eax
bool flag; // bp
bool ret_value; // al
const char *prompt; // rdi
int *rows; // [rsp+0h] [rbp-28h]
rows = 0LL;
puts("Input flag:");
scanf("%s", &input, 0LL);
if ( strlen(&input) != 24 || strncmp(&input, "nctf{", 5uLL) || *(&byte_6010BF + 24) != '}' )
{
wrong:
puts("Wrong flag!");
exit(-1);
}
i = 5LL;
if ( strlen(&input) - 1 > 5 )
{
while ( 1 )
{
a_ch = *(&input + i); // a_ch = input[i]
flag = 0;
if ( a_ch > 'N' )
{
a_ch = (unsigned __int8)a_ch;
if ( (unsigned __int8)a_ch == 'O' )
{
ret_value = left((_DWORD *)&rows + 1);// 列數減一
goto set_flag;
}
if ( a_ch == 'o' )
{
ret_value = right((int *)&rows + 1); // 列數加一
goto set_flag;
}
}
else
{
a_ch = (unsigned __int8)a_ch;
if ( (unsigned __int8)a_ch == '.' )
{
ret_value = up(&rows); // 行數減1
goto set_flag;
}
if ( a_ch == '0' )
{
ret_value = down((int *)&rows); // 行數加1
set_flag:
flag = ret_value;
goto LABEL_15;
}
}
LABEL_15:
if ( !(unsigned __int8)is_in_maze((__int64)maze, SHIDWORD(rows), (int)rows) )
goto wrong;
if ( ++i >= strlen(&input) - 1 ) // 走完所有迷宮
{
if ( flag )
break;
LABEL_20:
prompt = "Wrong flag!";
goto LABEL_21;
}
}
}
if ( maze[8 * (signed int)rows + SHIDWORD(rows)] != '#' )// cols = SHIWORD(rows)
goto LABEL_20;
prompt = "Congratulations!";
LABEL_21:
puts(prompt);
return 0LL;
}
/* Orphan comments:
高四位存放的是當前列數
*/
目的是找到’#'字符所在的位置,將maze的數據提取出來,實際上就是一個char數組,按照8*8排列,如下:
一開始處於(0,0)處,程序中用一個int64數來表示當前的位置,高位int表示的是列數,低位int表示的是行數。這樣就很容易分析其中具體函數的作用是改變行數還是列數了(如果你是真小白不懂的話找我問,我也是從完全不懂的小白過來的),分析出來輸入的字符和對應的操作應該是:
left:O
right:o
up:.
down:0
再結合前面的一些判斷的信息就可以得到flag了。