-
-
[原创] 第二题 南冥神功题解
-
2021-5-11 21:46 4119
-
使用ida打开pzcrackme.exe, 跳转到主函数F5, 首先看到输入, 将其重命名为input;
之后进入循环:
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 | if ( strlen( input ) < = 0x30 ) { inpc = input [ 0 ]; if ( input [ 0 ] ) { ipos = 0 ; y = 0 ; tx = 0 ; length = dword_4B7020; keyc = a0123456789abcd[ 0 ]; next : if ( length > 0 ) { keypos = 0 ; if ( keyc = = inpc ) { LABEL_11: ...(此处暂时省略) } else { while ( length ! = + + keypos ) { if ( a0123456789abcd[keypos] = = inpc ) goto LABEL_11; } } } } |
主体逻辑是循环找到a0123456789abcd字符串中输入字符所在的位置keypos之后进入下面的逻辑, 分析见注释:
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 | LABEL_11: v7 = (ipos + keypos / 6 ) % 6 ; v8 = keypos + ipos; x = tx; inp2 = v7; inp1 = 5 - v8 % 6 ; / / 将keypos + ipos 按 6 进制分解为inp1和inp2 for ( i = 0 ; ; i = 1 ) { switch ( inp1 ) / / 然后走迷宫 { case 1 : + + x; / / 右 break ; case 2 : v17 = (y + + & 1 ) = = 0 ; / / 偶行 右下 奇行 下 x + = v17; break ; case 3 : v12 = (y + + & 1 ) ! = 0 ; / / 偶行 下 奇行 左下 x - = v12; break ; case 4 : - - x; / / 左 break ; case 5 : v19 = (y - - & 1 ) ! = 0 ; / / 偶行 上 奇行 左上 x - = v19; break ; default: / / 偶行 右上 奇行 上 v18 = (y - - & 1 ) = = 0 ; x + = v18; break ; } if ( x > 9 ) / / 迷宫有 10 列 break ; if ( y > 8 ) / / 迷宫有 9 行 break ; v13 = &byte_4B7080[ 10 * y + x]; / / 迷宫数据在byte_4B7080 if ( * v13 ) break ; * v13 = 1 ; / / 只能走在数据为 0 的格子, 走过的位置设为 1 if ( i = = 1 ) { + + ipos; tx = x; inpc = input [ipos]; if ( inpc ) goto next ; goto fin; } inp1 = inp2; |
之后是验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | v14 = byte_4B7080; / / 迷宫数据 v15 = 0 ; do { v16 = v14 + 0xA ; do v15 + = * v14 + + = = 0 ; / / 如果迷宫数据中任意位是 0 则v15会增加 while ( v16 ! = v14 ); } while ( &unk_4B70DA ! = (_UNKNOWN * )v16 ); if ( !v15 ) / / 当v15没有增加时 成功 { sub_4ABF30(&dword_4B8860, "Good job!" , 9 ); sub_4AD980(&dword_4B8860); return 0 ; } |
所以就是要走迷宫把迷宫走成全是1的.
因为并没有分析它的一系列初始化函数, 怕哪里藏了坑然后静态的数据其实是骗你的, 所以接下来进行动态调试, 也为了绕过可能存在的反ida的调试措施(后来发现并没有) 所以使用frida进行动态调试.
根据分析结果写个frida agent脚本 (使用了typescript):
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 76 | const PE = Process.findModuleByName( "pzcrackme.exe" ); / / 输出迷宫 function show2d(data: NativePointer, width: number, height: number, showChar = false) { let text = ""; const map = new Uint8Array(data.readByteArray(width * height)); for (let y = 0 ; y < height; + + y) { for (let x = 0 ; x < width; + + x) { const chr = map [y * width + x]; if (showChar) text + = String.fromCharCode( chr ); else text + = ( "00" + chr .toString( 16 )).substr( - 2 ); text + = " " ; } text + = "\n" ; } console.log(text); } function inspect() { / / const keyLength = PE.base.add( 0xB7020 ).readUInt(); / / const keyBuf = PE.base.add( 0xB7040 ); / / console.log(`keyLength: ${keyLength}`); / / console.log(hexdump(keyBuf, {length: keyLength})); const mapBuf = PE.base.add( 0xB7080 ); show2d(mapBuf, 10 , 9 ); } / / 根据每一步想要选择的选项 0 ~ 5 逆向转换输入字符串 function convertInput(inp: string) { const keystr = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ; let i = 0 , ki = 0 , count = 0 ; let kx = 0 , ky = 0 ; let result = ''; for (let c of inp) { let target = c.charCodeAt( 0 ) - '0' .charCodeAt( 0 ); if (count % 2 = = 0 ) { / / target = 5 - (i + kx) % 6 kx = ( 5 - (target + i) % 6 ); } else { / / target = (i + ky) % 6 ky = (target + 6 * 100 - i) % 6 ; let key = ky * 6 + kx; result + = keystr[key]; } count + = 1 ; if (count % 2 = = 0 ) { i + = 1 ; } } return result; } let myInput = convertInput( "10" ); Interceptor.attach(PE.base.add( 0xB0AB0 ), { / / 输入函数 onEnter: function(args) { this.args = []; this.args[ 0 ] = ptr(args[ 0 ].toString()); this.args[ 1 ] = ptr(args[ 1 ].toString()); }, onLeave: function(retVal) { inspect(); (<NativePointer>this.args[ 1 ]).writeUtf8String(myInput); / / 模拟输入 } }); Interceptor.attach(PE.base.add( 0xABF30 ), { / / 输出函数 onEnter: function(args) { let logstr = args[ 1 ].readCString(); console.log(logstr); if (logstr = = 'Try again...' ) { inspect(); } } }); |
运行后输出如下:
起始在左上角, 可以自由向左右上下走, (从0开始算)在偶数行可以向右上或右下走, 在奇数行可以向左上或左下走, 可以手走一遍出结果, 为: convertInput("1234321234321101210050543450501210121234322321")
将其输出出来, 最后flag是GJ0V4LA4VKEVQZSVCNGJ00N
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
赞赏
他的文章
[原创]android 11 手动root笔记
38620
[原创] 第四题 英雄救美题解
5922
[原创] 第三题 统一门派非预期题解
4786
[原创] 第二题 南冥神功题解
4120
[原创] 签到题 拜师学艺题解
1919
看原图