RCTF把web狗打疯了,不想学web了.jpg
好吧来学学逆向算了
open-source 直接给了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <stdio.h> #include <string.h> int main (int argc, char *argv[]) { if (argc != 4 ) { printf ("what?\n" ); exit (1 ); } unsigned int first = atoi(argv[1 ]); if (first != 0xcafe ) { printf ("you are wrong, sorry.\n" ); exit (2 ); } unsigned int second = atoi(argv[2 ]); 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 = first * 31337 + (second % 17 ) * 11 + strlen (argv[3 ]) - 1615810207 ; printf ("Get your key: " ); printf ("%x\n" , hash); return 0 ; }
.\8b6405c25fe447fa804c6833a0d72808.exe 51966 25 h4cky0u
即可
simple-unpack ExeinfoPe看到是ELF文件,upx的壳,这几天有空的话可能会写一篇水文分析下upx
这里直接upx -d
脱掉,ida打开在字符串里就能看到flag
logmein ida f5反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void __fastcall __noreturn main (__int64 a1, char **a2, char **a3) { size_t v3; int i; char s[36 ]; int v6; __int64 v7; char v8[8 ]; int v9; v9 = 0 ; strcpy (v8, ":\"AL_RT^L*.?+6/46" ); v7 = 28537194573619560L L; 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) ) sub_4007C0(); for ( i = 0 ; i < strlen (s); ++i ) { if ( i >= strlen (v8) ) sub_4007C0(); if ( s[i] != (char )(*((_BYTE *)&v7 + i % v6) ^ v8[i]) ) sub_4007C0(); } sub_4007F0(); }
1 2 3 4 5 void __noreturn sub_4007C0 () { printf ("Incorrect password!\n" ); exit (0 ); }
1 2 3 4 5 void __noreturn sub_4007F0 () { printf ("You entered the correct password!\nGreat job!\n" ); exit (0 ); }
直接修改c代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <string.h> #define _BYTE char int main (__int64 a1, char **a2, char **a3) { size_t v3; int i; char s[36 ]; int v6; __int64 v7; char v8[18 ]; int v9; v9 = 0 ; strcpy (v8, ":\"AL_RT^L*.?+6/46" ); v7 = 28537194573619560L L; v6 = 7 ; for ( i = 0 ; i < 17 ; ++i ) { printf ("%c" ,(char )(*((_BYTE *)&v7 + i % v6) ^ v8[i])); } return 0 ; }
用python写1 2 3 4 5 6 7 8 9 10 key1 = ":\"AL_RT^L*.?+6/46" key2="72616865626d6172616865626d61726168" j = -2 for i in range(0 ,17 ): temp = int('0x' +key2[j:][:2 ],16 ) j = j-2 result = chr(temp ^ ord(key1[i])) print(result,end='' )
insanity 32位的ELF文件,字符串里就有flag
python-trade python字节码逆向
1 uncompyle6 f417c0d03b0344eb9969ed0e1f772091.pyc > source.py
得到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # uncompyle6 version 3.6.4 # Python bytecode 2.7 (62211) # Decompiled from: Python 3.7.4 (default, Jul 11 2019, 10:43:21) # [GCC 8.3.0] # Embedded file name: 1.py # Compiled at: 2017-06-03 10:20:43 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' # okay decompiling f417c0d03b0344eb9969ed0e1f772091.pyc
按程序逻辑解码即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import base64def decode (message) : message = base64.b64decode(message) flag='' for i in message: x = ord(i) x -= 16 result = x^32 flag += chr(result) return flag message = 'XlNkVmtUI1MgXWBZXCFeKY+AaXNt' print(decode(message))
game 一个小游戏,需要把所有的灯都打开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 void main_0 () { signed int i; int v1; sub_45A7BE(&unk_50B110); sub_45A7BE(&unk_50B158); sub_45A7BE(&unk_50B1A0); sub_45A7BE(&unk_50B1E8); sub_45A7BE(&unk_50B230); sub_45A7BE(&unk_50B278); sub_45A7BE(&unk_50B2C0); sub_45A7BE(&unk_50B308); sub_45A7BE(&unk_50AFD0); sub_45A7BE("| by 0x61 |\n" ); sub_45A7BE("| |\n" ); sub_45A7BE("|------------------------------------------------------|\n" ); sub_45A7BE( "Play a game\n" "The n is the serial number of the lamp,and m is the state of the lamp\n" "If m of the Nth lamp is 1,it's on ,if not it's off\n" "At first all the lights were closed\n" ); sub_45A7BE("Now you can input n to change its state\n" ); sub_45A7BE( "But you should pay attention to one thing,if you change the state of the Nth lamp,the state of (N-1)th and (N+1)th w" "ill be changed too\n" ); sub_45A7BE("When all lamps are on,flag will appear\n" ); sub_45A7BE("Now,input n \n" ); while ( 1 ) { while ( 1 ) { sub_45A7BE("input n,n(1-8)\n" ); sub_459418(); sub_45A7BE("n=" ); sub_4596D4("%d" , &v1); sub_45A7BE("\n" ); if ( v1 >= 0 && v1 <= 8 ) break ; sub_45A7BE("sorry,n error,try again\n" ); } if ( v1 ) { sub_4576D6(v1 - 1 ); } else { for ( i = 0 ; i < 8 ; ++i ) { if ( (unsigned int )i >= 9 ) j____report_rangecheckfailure(); byte_532E28[i] = 0 ; } } j__system("CLS" ); sub_458054(); if ( byte_532E28[0 ] == 1 && byte_532E28[1 ] == 1 && byte_532E28[2 ] == 1 && byte_532E28[3 ] == 1 && byte_532E28[4 ] == 1 && byte_532E28[5 ] == 1 && byte_532E28[6 ] == 1 && byte_532E28[7 ] == 1 ) { sub_457AB4(); } } }
其中这一段是在判断所有的灯是否打开1 2 3 4 5 6 7 8 9 10 11 if ( byte_532E28[0 ] == 1 && byte_532E28[1 ] == 1 && byte_532E28[2 ] == 1 && byte_532E28[3 ] == 1 && byte_532E28[4 ] == 1 && byte_532E28[5 ] == 1 && byte_532E28[6 ] == 1 && byte_532E28[7 ] == 1 ) { sub_457AB4(); }
sub_457AB4
会调用sub_45E940
计算并输出flag
接下来方法有很多:
修改汇编,运行至sub_457AB4();
根据游戏规则,暴力穷举可行解
根据sub_45E940
的逻辑算出flag
这里用最快的第一种方法
找到对应汇编 找到对应下面这段代码的汇编
1 2 3 4 5 6 7 8 9 10 11 if ( byte_532E28[0 ] == 1 && byte_532E28[1 ] == 1 && byte_532E28[2 ] == 1 && byte_532E28[3 ] == 1 && byte_532E28[4 ] == 1 && byte_532E28[5 ] == 1 && byte_532E28[6 ] == 1 && byte_532E28[7 ] == 1 ) { sub_457AB4(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 text: 0045F5D1 movzx edx , byte_532E28[ecx ].text: 0045F5D8 cmp edx , 1 .text: 0045F5DB jnz loc_45F671.text: 0045F5E1 mov eax , 1 .text: 0045F5E6 shl eax , 0 .text: 0045F5E9 movzx ecx , byte_532E28[eax ].text: 0045F5F0 cmp ecx , 1 .text: 0045F5F3 jnz short loc_45F671.text: 0045F5F5 mov eax , 1 .text: 0045F5FA shl eax , 1 .text: 0045F5FC movzx ecx , byte_532E28[eax ].text: 0045F603 cmp ecx , 1 .text: 0045F606 jnz short loc_45F671.text: 0045F608 mov eax , 1 .text: 0045F60D imul ecx , eax , 3 .text: 0045F610 movzx edx , byte_532E28[ecx ].text: 0045F617 cmp edx , 1 .text: 0045F61A jnz short loc_45F671.text: 0045F61C mov eax , 1 .text: 0045F621 shl eax , 2 .text: 0045F624 movzx ecx , byte_532E28[eax ].text: 0045F62B cmp ecx , 1 .text: 0045F62E jnz short loc_45F671.text: 0045F630 mov eax , 1 .text: 0045F635 imul ecx , eax , 5 .text: 0045F638 movzx edx , byte_532E28[ecx ].text: 0045F63F cmp edx , 1 .text: 0045F642 jnz short loc_45F671.text: 0045F644 mov eax , 1 .text: 0045F649 imul ecx , eax , 6 .text: 0045F64C movzx edx , byte_532E28[ecx ].text: 0045F653 cmp edx , 1 .text: 0045F656 jnz short loc_45F671.text: 0045F658 mov eax , 1 .text: 0045F65D imul ecx , eax , 7 .text: 0045F660 movzx edx , byte_532E28[ecx ].text: 0045F667 cmp edx , 1 .text: 0045F66A jnz short loc_45F671.text: 0045F66C call sub_457AB4
可以看到这里进行了多次cmp
比较,不符合要求就jnz
跳走,全部符合就执行call sub_457AB4
那么直接在x64dbg或者ollydbg里把jnz
改成nop就好
关闭aslr 由于aslr的存在,程序运行时会发生基址重定位。因此x64dbg和ollydbg里程序的基址和ida里是不同的,可以使用loadpe关掉exe的aslr
选择Characteristics,勾选第一项Relocation stripped然后save即可
修改汇编
把8个jnz
都改成nop,运行到最后的call即可
Hello, CTF
反汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 int __cdecl main (int argc, const char **argv, const char **envp) { signed int v3; char v4; int result; int v6; int v7; char v8; char v9[20 ]; char v10; __int16 v11; char v12; char v13; strcpy (&v13, "437261636b4d654a757374466f7246756e" ); while ( 1 ) { memset (&v10, 0 , 0x20 u); 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) ) sub_40134B(aSuccess, v7); else sub_40134B(aWrong, v7); } sub_40134B(aWrong, v7); result = stru_408090._cnt-- - 1 ; if ( stru_408090._cnt < 0 ) return _filbuf(&stru_408090); ++stru_408090._ptr; return result; }
将用户输入转成16进制与437261636b4d654a757374466f7246756e对比,把437261636b4d654a757374466f7246756e转成字符串即可
getit 64位的ELF文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 int __cdecl main (int argc, const char **argv, const char **envp) { char v3; __int64 v5; int i; FILE *stream; char filename[8 ]; unsigned __int64 v9; v9 = __readfsqword(0x28 u); 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, 0L L, 0 ); fprintf (stream, "%s\n" , u); } fclose(stream); remove (filename); return 0 ; }
gdb调试下发现经过第一个while
后t
指向的字符串的就是flag….
程序的逻辑有空再写…(咕了)
re1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int __cdecl main (int argc, const char **argv, const char **envp) { int v3; __int128 v5; __int64 v6; int v7; __int16 v8; char v9; _mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((const __m128i *)&xmmword_413E34)); v7 = 0 ; v6 = qword_413E44; v8 = 0 ; printf (&byte_413E4C); printf (&byte_413E60); printf (&byte_413E80); scanf ("%s" , &v9); v3 = strcmp ((const char *)&v5, &v9); if ( v3 ) v3 = -(v3 < 0 ) | 1 ; if ( v3 ) printf (aFlag); else printf ((const char *)&unk_413E90); system("pause" ); return 0 ; }
逻辑很明显,将用户输入v9
与v5
对比,正确则输出aFlag
_mm_storeu_si128((__m128i *)&v5, _mm_loadu_si128((const __m128i *)&xmmword_413E34));
类似于一个memset
,将xmmword_413E34
的值赋给v5。实际上xmmword_413E34
就是flag,不过ida会将其识别为16进制数
用R指令转为字符串
因为是小端存储所以要倒过来
用x64dbg的话直接就会识别出字符串…
no-strings-attached 不知道为啥运行会报错..
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { setlocale(6 , &locale); banner(); prompt_authentication(); authenticate(); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void authenticate () { int ws[8192 ]; wchar_t *s2; s2 = decrypt(&s, &dword_8048A90); if ( fgetws(ws, 0x2000 , stdin ) ) { ws[wcslen(ws) - 1 ] = 0 ; if ( !wcscmp(ws, s2) ) wprintf(&unk_8048B44); else wprintf(&unk_8048BA4); } free (s2); }
decrypt()
中会计算flag1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 wchar_t *__cdecl decrypt (wchar_t *s, wchar_t *a2) { size_t v2; signed int v4; signed int i; signed int v6; signed int v7; wchar_t *dest; v6 = wcslen(s); v7 = wcslen(a2); v2 = wcslen(s); dest = (wchar_t *)malloc (v2 + 1 ); wcscpy(dest, s); while ( v4 < v6 ) { for ( i = 0 ; i < v7 && v4 < v6; ++i ) dest[v4++] -= a2[i]; } return dest; }
s2 = decrypt(&s, &dword_8048A90);
对应的汇编是1 2 .text: 08048720 call decrypt.text: 08048725 mov [ebp +s2], eax
gdb 断在0x8048725
可以看到eax存储的是一个地址,该地址存储了一个以9开头的字符串,查看该地址即可得到flag
csaw2013reversing2 运行会出现乱码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; CHAR *lpMem; HANDLE hHeap; hHeap = HeapCreate(0x40000 u, 0 , 0 ); lpMem = (CHAR *)HeapAlloc(hHeap, 8u , MaxCount + 1 ); memcpy_s(lpMem, MaxCount, &unk_409B10, MaxCount); if ( sub_40102A() || IsDebuggerPresent() ) { __debugbreak(); sub_401000(v3 + 4 , (int )lpMem); ExitProcess(0xFFFFFFFF ); } MessageBoxA(0 , lpMem + 1 , "Flag" , 2u ); HeapFree(hHeap, 0 , lpMem); HeapDestroy(hHeap); ExitProcess(0 ); }
这里会通过IsDebuggerPresent()
判断是否为调试器运行,正常运行就显示上面乱码的窗口
sub_401000(v3 + 4, (int)lpMem)
将乱码转换为flag1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 unsigned int __fastcall sub_401000 (int a1, int a2) { int v2; unsigned int v3; unsigned int v4; unsigned int result; v2 = dword_409B38; v3 = a2 + 1 + strlen ((const char *)(a2 + 1 )) + 1 ; v4 = 0 ; result = ((v3 - (a2 + 2 )) >> 2 ) + 1 ; if ( result ) { do *(_DWORD *)(a2 + 4 * v4++) ^= v2; while ( v4 < result ); } return result; }
那么尝试x64dbg跟进去
这里的int3
应该对应于ida反汇编出的__debugbreak();
,会使程序在这里死循环,改成nop即可
运行完sub_401000
得到flag
实际上一进到sub_401000
x64dbg就在第三行的注释那里显示了flag…但是跟一下程序逻辑会发现flag是在后面0x0040101F的循环中计算并写入0x23d05c9的(这个地址是堆的地址,每次都不同),看起来是x64dbg提前计算了出来?
maze 迷宫问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { signed __int64 i; signed int v4; bool status; bool result; const char *v7; __int64 v9; v9 = 0L L; puts ("Input flag:" ); scanf ("%s" , &input, 0L L); if ( strlen (&input) != 24 || strncmp (&input, "nctf{" , 5u LL) || *(&byte_6010BF + 24 ) != '}' ) { WRONG_FLAG: puts ("Wrong flag!" ); exit (-1 ); } i = 5L L; if ( strlen (&input) - 1 > 5 ) { while ( 1 ) { v4 = *(&input + i); status = 0 ; if ( v4 > 'N' ) { v4 = (unsigned __int8)v4; if ( (unsigned __int8)v4 == 'O' ) { result = sub_400650((_DWORD *)&v9 + 1 ); goto LABEL_14; } if ( v4 == 'o' ) { result = sub_400660((int *)&v9 + 1 ); goto LABEL_14; } } else { v4 = (unsigned __int8)v4; if ( (unsigned __int8)v4 == '.' ) { result = sub_400670(&v9); goto LABEL_14; } if ( v4 == '0' ) { result = sub_400680((int *)&v9); LABEL_14: status = result; goto LABEL_15; } } LABEL_15: if ( !(unsigned __int8)check((__int64)maze, SHIDWORD(v9), v9) ) goto WRONG_FLAG; if ( ++i >= strlen (&input) - 1 ) { if ( status ) break ; WRONG_FLAG2: v7 = "Wrong flag!" ; goto OUTPUT ; } } } if ( maze[8 * (signed int )v9 + SHIDWORD(v9)] != '#' ) goto WRONG_FLAG2; v7 = "Congratulations!" ; OUTPUT : puts (v7); return 0L L; }
asc_601060
用字符串表示了一个8x8迷宫1 db ' ******* * **** * **** * *** *# *** *** *** *********' ,0
转换成8x8迷宫1 2 3 4 5 6 7 8 9 maze = " ******* * **** * **** * *** *# *** *** *** *********" for i in range(64 ): if i%8 ==0 : print("\n" ) if maze[i]==' ' : print('0' ,end='' ) else : print(maze[i],end='' )
1 2 3 4 5 6 7 8 00****** *000*00* ***0*0** **00*0** *00*#00* **0***0* **00000* ********
v9
表示当前所处位置,(int *)&v9
是行,(int *)&v9 + 1
和HIDWORD(v9)
是列
其中SHIDWORD
是ida逆向常用的宏,定义为
1 #define SHIDWORD(x) (*((int32*)&(x)+1))
最后把迷宫的走法转成对应的字符就可以得到flag1 2 3 4 5 6 7 8 9 str = "右下右右下下左下下下右右右右上上左左" str = str.replace('上' , '.' ) str = str.replace('下' , '0' ) str = str.replace('左' , 'O' ) str = str.replace('右' , 'o' ) str = 'nctf{' + str + '}' print(str)