-
-
[原创]看雪 2022·KCTF 秋季赛 > 第六题 病疫先兆 by 心学
-
2022-11-28 23:05 10301
-
工具:IDA、Python
一、观察程序运行结果
1 2 3 4 5 6 7 | C:\Users\surface>C:\Users\surface\OneDrive\Crack\CTF\Kanxue2022KCTFAutumn\ 06 \CrackMe\CrackMe.exe please input : 0123456789 error 请按任意键继续. . . C:\Users\surface> |
代码错误时:输出error
二、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 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 | int __cdecl main( int argc, const char * * argv, const char * * envp) { int preValue; / / eax unsigned int preValueCopy; / / ebx int sufValue; / / eax unsigned int sufValueCopy; / / edi int * v7; / / esi int * v8; / / esi char inputSN[ 128 ]; / / [esp + 4h ] [ebp - A0h] BYREF char sufStr[ 8 ]; / / [esp + 84h ] [ebp - 20h ] BYREF char preStr[ 8 ]; / / [esp + 8Ch ] [ebp - 18h ] BYREF int v13; / / [esp + 94h ] [ebp - 10h ] int v14; / / [esp + 98h ] [ebp - Ch] int v15; / / [esp + 9Ch ] [ebp - 8h ] memset(inputSN, 0 , sizeof(inputSN)); printf( "please input :\n" ); scanf_s( "%s" , inputSN); if ( sub_B91000(inputSN) ! = 0xE ) goto LABEL_19; preStr[ 5 ] = 0 ; / / 字符串截断符:只允许 5 个字节 * (_DWORD * )preStr = * (_DWORD * )inputSN; preStr[ 4 ] = inputSN[ 4 ]; preValue = atoi(preStr); v15 = * (_DWORD * )&inputSN[ 5 ]; sufStr[ 5 ] = 0 ; / / 字符串截断符:只允许 5 个字节 * (_DWORD * )sufStr = * (_DWORD * )&inputSN[ 9 ]; preValueCopy = preValue; sufStr[ 4 ] = inputSN[ 0xD ]; sufValue = atoi(sufStr); v13 = 0 ; sufValueCopy = sufValue; v14 = 1 ; / / 需保证为 1 srand(preValueCopy); v7 = dword_B9F000; while ( rand() = = * v7 ) / / 依次获取的随机值需与内置全局数组相等 { if ( ( int ) + + v7 > = ( int )dword_B9F050 ) goto LABEL_7; / / 要跳出来。避开 v14 = 0 } v14 = 0 ; / / 执行了这一步就错 LABEL_7: srand(sufValueCopy); v8 = dword_B9F050; while ( rand() = = * v8 ) / / 依次获取的随机值需与内置全局数组相等 { if ( ( int ) + + v8 > = ( int )&dword_B9F0A0 ) goto LABEL_12; / / 要跳出来。避开 v14 = 0 } v14 = 0 ; / / 执行了这一步就错 LABEL_12: if ( (_BYTE)v15 = = 'K' && * (_WORD * )((char * )&v15 + 1 ) = = 'TC' && HIBYTE(v15) = = 'F' ) / / KCTF v13 = 1 ; if ( v14 && v13 ) { printf( "success : %s\n" , inputSN); system( "pause" ); } else { LABEL_19: printf( "error\n" ); system( "pause" ); } return 0 ; } |
初始化随机种子
1 2 3 4 | void __cdecl srand(unsigned int Seed) { * (_DWORD * )(_getptd() + 0x14 ) = Seed; } |
随机函数
1 2 3 4 5 6 7 8 9 10 | int __cdecl rand() { int v0; / / ecx unsigned int v1; / / eax v0 = _getptd(); v1 = 0x343FD * * (_DWORD * )(v0 + 0x14 ) + 0x269EC3 ; * (_DWORD * )(v0 + 0x14 ) = v1; return HIWORD(v1) & 0x7FFF ; } |
程序整理的逻辑结构清晰,最终通过 if ( v14 && v13 ) 之后,才判断正确
三、整理加密逻辑
1、获取用户字符串
2、检查字符串长度:0xE = 14
1 | if ( sub_B91000(inputSN) ! = 0xE ) |
3、将字符串分为三部分
第一部分:前5个字符,转为整数
1 2 3 4 | preStr[ 5 ] = 0 ; / / 字符串截断符:只允许 5 个字节 * (_DWORD * )preStr = * (_DWORD * )inputSN; preStr[ 4 ] = inputSN[ 4 ]; preValue = atoi(preStr); |
第二部分:KCTF
1 | v15 = * (_DWORD * )&inputSN[ 5 ]; |
第三部分:后5个字符,转为整数
1 2 3 4 5 6 | v15 = * (_DWORD * )&inputSN[ 5 ]; sufStr[ 5 ] = 0 ; / / 字符串截断符:只允许 5 个字节 * (_DWORD * )sufStr = * (_DWORD * )&inputSN[ 9 ]; preValueCopy = preValue; sufStr[ 4 ] = inputSN[ 0xD ]; sufValue = atoi(sufStr); |
4、校验
第一部分:用转换后的整数初始化种子,依次获取20个数,与内置全局数组相等
1 2 3 4 5 6 7 8 | srand(preValueCopy); v7 = dword_B9F000; while ( rand() = = * v7 ) / / 依次获取的随机值需与内置全局数组相等 { if ( ( int ) + + v7 > = ( int )dword_B9F050 ) goto LABEL_7; / / 要跳出来。避开 v14 = 0 } v14 = 0 ; / / 执行了这一步就错 |
第二部分:KCTF
1 | if ( (_BYTE)v15 = = 'K' && * (_WORD * )((char * )&v15 + 1 ) = = 'TC' && HIBYTE(v15) = = 'F' ) / / KCTF |
第三部分:用转换后的整数初始化种子,依次获取20个数,与内置全局数组相等
1 2 3 4 5 6 7 8 | srand(sufValueCopy); v8 = dword_B9F050; while ( rand() = = * v8 ) / / 依次获取的随机值需与内置全局数组相等 { if ( ( int ) + + v8 > = ( int )&dword_B9F0A0 ) goto LABEL_12; / / 要跳出来。避开 v14 = 0 } v14 = 0 ; / / 执行了这一步就错 |
四、编写破解代码
构造序列号为:XXXXXKCTFYYYYY
直接采取爆破的方式,分别获取前后的5个数字字符。
5位数值依次传给种子,逐个生成,并与内置数值比较,成功即记录并退出循环。
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 | ''' 日期:2022-11-28 作者:htg.心学 ''' ########################################################### seed = 0 ###初始化种子 def InitSand(sd): global seed seed = sd ###获取随机值 def GetSand(): global seed seed = seed * 0x343FD seed = seed & 0xFFFFFFFF seed = seed + 0x269EC3 seed = seed & 0xFFFFFFFF returnValue = seed >> 0x10 returnValue = returnValue & 0x7FFF return returnValue ###两个列表 preList = [ 0x00003BFC , 0x00002173 , 0x000025BB , 0x0000380B , 0x00002C13 , 0x000075BE , 0x00007366 , 0x000046A3 , 0x000013C1 , 0x0000159B , 0x00005B5F , 0x0000534F , 0x00004E37 , 0x00003A04 , 0x00001301 , 0x00005D0C , 0x00004155 , 0x000048E9 , 0x000061D2 , 0x00006158 ] sufList = [ 0x00002BB6 , 0x00006B5A , 0x000003D4 , 0x0000152B , 0x00006E04 , 0x0000254C , 0x000040AE , 0x000056CA , 0x000017E1 , 0x000055C7 , 0x00003641 , 0x00002D3C , 0x00000A41 , 0x00004BC5 , 0x00006233 , 0x00001FE7 , 0x00006E05 , 0x00000F6E , 0x00006398 , 0x00006AD7 ] ########################################################### ######三部分:XXXXXKCTFYYYYY ##第一部分:XXXXX ##第二部分:KCTF ##第三部分:YYYYY ########################################################### preFound = False sufFound = False print ( "寻找第一部分......" ) for i in range ( 100000 ): InitSand(i) preFound = True for a in preList: if GetSand() ! = a: preFound = False if preFound: print ( "\t找到第一部分:{:05}" . format (i)) break if not preFound: print ( "Failed!" ) ########################################################### print ( "寻找第三部分......" ) for j in range ( 100000 ): InitSand(j) sufFound = True for a in sufList: if GetSand() ! = a: sufFound = False if sufFound: print ( "\t找到第三部分:{:05}" . format (j)) break if not sufFound: print ( "Failed!" ) SN = "{:05}KCTF{:05}" . format (i,j) print ( "序列号:{}" . format (SN)) ########################################################### |
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
赞赏
他的文章
看原图