XCTF-RE
前言 放假两天了,在家实在闲的太无聊了,把攻防世界新手区的博客给水完吧。啥也不说,直接上题!
0x01 Hello, CTF
32位exe文件,直接暴力拖入IDA 32-bit中,暴力F5查看关键代码如下
strcpy(&v13, "437261636b4d654a757374466f7246756e" ); while ( 1 ) { memset(&v10, 0 , 0x20 u); //v10字符串初始化 v11 = 0 ; v12 = 0 ; sub_40134B(aPleaseInputYou, v6); scanf(aS, v9); if ( strlen(v9) > 0x11 ) break ; v3 = 0 ; do { v4 = v9[v3]; if ( !v4 ) break ; sprintf(&v8, asc_408044, v4); strcat(&v10, &v8); ++v3; } while ( v3 < 17 ); if ( !strcmp(&v10, &v13) ) //如果v10=v13 则输出success sub_40134B(aSuccess, v7); else sub_40134B(aWrong, v7); //否则输出wrong } sub_40134B(aWrong, v7); result = stru_408090._cnt-- - 1 ; if ( stru_408090._cnt < 0 ) return _filbuf(&stru_408090); ++stru_408090._ptr; return result; }
这里我们看到,当v10和v13相等时,会返回success ,接下来要分析的就是v10是怎么来的,通过上面的代码可以知道v10 是通过将输入的字符串以十六进制读取而成的 那么,我们将v13转换成10进制(两位一转),再通过Ascll码变成字符,就可以得到正确的结果,编写脚本如下:
a = [0x43 ,0x72 ,0x61 ,0x63 ,0x6b ,0x4d ,0x65 ,0x4a ,0x75 ,0x73 ,0x74 ,0x46 ,0x6f ,0x72 ,0x46 ,0x75 ,0x6e ] c = "" for x in a: c += chr(x) print(c)
得到flag{CrackMeJustForFun}
0x02 getit IDA静态分析,F5查看关键代码
int __cdecl main(int argc, const char **argv, const char **envp) { char v3; // al __int64 v5; // [rsp+0 h] [rbp-40 h] int i; // [rsp+4 h] [rbp-3 Ch] FILE *stream; // [rsp+8 h] [rbp-38 h] char filename[8 ]; // [rsp+10 h] [rbp-30 h] unsigned __int64 v9; // [rsp+28 h] [rbp-18 h] v9 = __readfsqword(0x28 u); LODWORD(v5) = 0 ; while ( (signed int)v5 < strlen(s) ) // s=c61b68366edeb7bdce3c6820314b7498 { if ( v5 & 1 ) v3 = 1 ; else v3 = -1 ; *(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3; //t=53h+harifCTF{????????????????????????????????} 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, 0L L, 0 ); fprintf(stream, "%s\n" , u); } fclose(stream); remove(filename); return 0 ; }
s = ‘c61b68366edeb7bdce3c6820314b7498’ t = [‘S’,’h’,’a’,’r’,’i’,’f’,’C’,’T’,’F’,’{‘,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’?’,’}’]
编写不同语言脚本如下
v5 = 0 //python 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)
#include <stdio.h> //c语言 #include <stdlib.h> #include <Windows.h> #pragma warning (disable:4996) int main (void ) { char v3; __int64 v5; char s[] = "c61b68366edeb7bdce3c6820314b7498" ; char t[] = "SharifCTF{????????????????????????????????}" ; v5 = 0 ; while (v5 < strlen (s)) { if (v5 & 1 ) v3 = 1 ; else v3 = -1 ; *(t + v5 + 10 ) = s[v5] + v3; v5++; } printf ("%s" , t); system("PAUSE" ); return 0 ; }
#include <iostream> //c++ using namespace std ;int main () { char s[34 ]={"c61b68366edeb7bdce3c6820314b7498" }; char content[34 ]; int a; for (int i=0 ;i<33 ;i++) { if (i&1 ) a=1 ; else a=-1 ; content[i]=s[i]+a; } cout <<content; return 0 ; }
0x03 re1 非常简单入门的逆向题
直接拖入Winhex
搜索字符串CTF
就得到flag了
0x04 no-strings-attached 这块的知识点还不咋会,先不写,等我学会了再来写吧
0x05 csaw2013reversing2 不多逼逼,直接使用 IDA Pro 7.0 (32 bit) 打开程序,默认进入主函数的反汇编窗口,按下 F5 后进行反编译,自动生成类 C 语言的伪代码:
可已见到,若 sub_40102A()
或 IsDebuggerPresent()
的返回值为真,则执行调试断点指令 __debugbreak()
、子函数 sub_401000(v3 + 4, lpMem)
、结果进程函数 ExitProcess(0xFFFFFFFF)
,否则直接执行 MessageBoxA(0, lpMem + 1, "Flag", 2u)
,弹出全是乱码的 Flag 提示框。
双击 sub_40102A()
查看其反编译代码,发现返回值恒为零:
int sub_40102A () { char v0; v0 = *(_BYTE *)(*(_DWORD *)(__readfsdword(0x18 u) + 48 ) + 2 ); return 0 ; }
而对于库函数 IsDebuggerPresent()
,若程序处于调试模式下,则返回值为非零;若未处于调试模式下,则返回值为零。显然,程序不处于调试模式下,即无法满足 if
语句的条件。
双击 sub_401000()
查看其反编译代码,目测是对以上乱码数据的解密函数:
综上,解题思路大致为:进入 if
语句块,跳过调试断点,并执行解密函数,最终弹框输出 Flag。
在主函数的反汇编窗口中,核心的语句块如下:
首先,int 3
中断即为调试断点指令,需将其改为空指令 nop
。
将光标置于中断指令所在行,依次点击 Edit -> Patch program -> Assemble ,弹出指令修改框:
将 int 3
改为 nop
后点击 OK 即可。
小贴士:点击 OK 后,IDA 会自动弹出下一条指令的修改框,通常无需修改,点击 Cancel 即可。
根据上述操作,依次将 jmp short loc_4010EF
修改为 jmp short loc_4010B9
,将 jz short loc_4010B9
修改为 jmp short loc_401096
,修改完运行流程如下
最后,依次点击 Edit -> Patch program -> Apply patches to input file… ,弹出设置框,选择待打补丁程序,按需选择对原程序进行备份,设置完毕后点击 OK 即可。找到修改后的程序,双击执行可获得 flag{reversing_is_not_that_hard!}
0x06 maze 这道题之前用IDA打开过,但是整来整去连源码都没找出来,也不知道和迷宫有什么关系。直到上次安恒6月赛题也出了一道类似的迷宫题我才恍然大悟!还是要多做题,兄弟们,没经验基础真滴是不行的。
这是个64位的elf文件,放IDA里看一下main函数,部分代码加了注释
__int64 __fastcall main (__int64 a1, char **a2, char **a3) { const char *v3; signed __int64 v4; signed int v5; char v6; char v7; const char *v8; __int64 v10; v10 = 0L L; puts ("Input flag:" ); scanf ("%s" , &s1, 0L L); if ( strlen (&s1) != 24 || (v3 = "nctf{" , strncmp (&s1, "nctf{" , 5u LL)) || *(&byte_6010BF + 24 ) != 125 ) { LABEL_22: puts ("Wrong flag!" ); exit (-1 ); } v4 = 5L L; if ( strlen (&s1) - 1 > 5 ) { while ( 1 ) { v5 = *(&s1 + v4); v6 = 0 ; if ( v5 > 78 ) { v5 = (unsigned __int8)v5; if ( (unsigned __int8)v5 == 'O' ) { v7 = sub_400650((_DWORD *)&v10 + 1 ); goto LABEL_14; } if ( v5 == 'o' ) { v7 = sub_400660((char *)&v10 + 4 , v3); goto LABEL_14; } } else { v5 = (unsigned __int8)v5; if ( (unsigned __int8)v5 == '.' ) { v7 = sub_400670(&v10, v3); goto LABEL_14; } if ( v5 == '0' ) { v7 = sub_400680(&v10, v3); LABEL_14: v6 = v7; goto LABEL_15; } } LABEL_15: v3 = (const char *)HIDWORD(v10); if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v10), v10) ) goto LABEL_22; if ( ++v4 >= strlen (&s1) - 1 ) { if ( v6 ) break ; LABEL_20: v8 = "Wrong flag!" ; goto LABEL_21; } } } if ( asc_601060[8 * (signed int )v10 + SHIDWORD(v10)] != 35 ) goto LABEL_20; v8 = "Congratulations!" ; LABEL_21: puts (v8); return 0L L; }
下边全是goto语句 我们直接将视图切为图表(Graph view)
按r可以发现,根据这四个字符”. “ ,”0 “,”o “ , “O “分别跳到不同的位置进行操作 ,既然sub_400650 ,sub_400660 ,sub_400670 ,sub_400680 分别是写左右上下的函数,那我们就看看sub_400690 到底是干啥的呢!
我们可以看到sub_400690 是和asc_601060 有关,猜测是判断上面asc_601060 数组的第edi个值是否等于20h或23h,如果不等于就跳到
loc_400822 输出wrong flag !
我们直接去Hex中找到地址为601060 中的数据!发现恰好为一个八阶方阵,而且数值只有3个,分别为20h,2Ah以及23h
那么函数400690 的意思不就是判断当前的位置是否是从(0,0)走到(4,4)
最初的四个跳转应该就是对应着上下左右四个方向
20 20 2A 2A 2A 2A 2A 2A
2A 20 20 20 2A 20 20 2A
2A 2A 2A 20 2A 20 2A 2A
2A 2A 20 20 2A 20 2A 2A
2A 20 20 2A 23 20 20 2A
2A 2A 20 2A 2A 2A 20 2A
2A 2A 20 20 20 20 20 2A
2A 2A 2A 2A 2A 2A 2A 2A
那么上下左右分别对应字符”.” “0” “O” “o”
路径为:右下右右下下左下下下右右右右上上左左 o0oo00O000oooo..OO
我们得到flag为nctf{o0oo00O000oooo..OO}