-
-
[原创]KCTF 2023 第三题 解题过程
-
2023-9-6 22:44 8136
-
以为又是个算法题,没想到是个逆向加脑洞题
1.获取vm代码
字符串特征 cryptopp、unicorn,加上code.dat文件,分别从输入和unicorn入手
从xml可以找到check_va,引用找到输入获取逻辑,只能确认输入长度限制32位
从code.dat(utf-16)引用可以找到unicorn的调用函数,结合unicorn官方文档,推测出函数调用,关键在写入代码和数据的 uc_mem_write uc_mem_read。
ida启动调试、附加全部崩溃在invalidHandle,推测有反调试。
windows反调试不太熟,于是启动frida
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | function seeHexA(addr, length) { console.log(hexdump(ptr(addr), { length: parseInt(length) })) } var base = Module.getBaseAddress( "ctf_app.exe" ) / / Interceptor.attach(ptr( 0x00BC28E0 + parseInt(base) - 0xBC0000 ), / / { / / onEnter: function (args) { / / console.log(Process.getCurrentThreadId(), this.context.ecx, this.context.esp.add( 4 ).readPointer(), "caller =" , this.context.esp.readPointer().sub(base)) / / / / console. log ( ' Context : ' + JSON. stringify (this. context)); / / console.log(Process.getCurrentThreadId(), "string_from_u16:" , this.context.esp.add( 4 ).readPointer().readUtf16String()) / / console.log() / / / / seeHexA(this.context.esp) / / console.log(Process.getCurrentThreadId(), "------------------------" ) / / } / / }) / / Interceptor.attach(ptr( 0xBC28C0 + parseInt(base) - 0xBC0000 ), / / { / / onEnter: function (args) { / / console.log(Process.getCurrentThreadId(), "wrap_SendMessageW " / / , this.context.esp.add( 0x4 ).readPointer() / / , this.context.esp.add( 0x8 ).readPointer() / / , this.context.esp.add( 0xc ).readPointer() / / , this.context.esp.add( 0x10 ).readPointer() / / , "caller =" , this.context.esp.readPointer().sub(base) / / ) / / console.log() / / / / seeHexA(this.context.esp) / / console.log(Process.getCurrentThreadId(), "------------------------" ) / / } / / } / / ) / / Interceptor.attach(ptr( 0xBC6850 + parseInt(base) - 0xBC0000 ), / / { / / onEnter: function (args) { / / console.log(Process.getCurrentThreadId(), "enc1 " / / , this.context.ecx / / , this.context.edx / / , this.context.ebp / / , this.context.esp.add( 0x4 ).readPointer() / / , "caller =" , this.context.esp.readPointer() / / ) / / console.log() / / / / seeHexA(this.context.esp) / / console.log(Process.getCurrentThreadId(), "------------------------" ) / / } / / } / / ) / / Interceptor.attach(ptr( 0xBC6D20 + parseInt(base) - 0xBC0000 ), / / { / / onEnter: function (args) { / / console.log(Process.getCurrentThreadId(), "enc2 " / / , this.context.edx / / , this.context.ecx / / , this.context.ebp / / , this.context.edi / / , this.context.esi / / , this.context.esp.add( 0x4 ).readPointer() / / , "caller =" , this.context.esp.readPointer().sub(base) / / ) / / console.log() / / / / seeHexA(this.context.esp) / / console.log(Process.getCurrentThreadId(), "------------------------" ) / / } / / } / / ) / / Interceptor.attach(ptr( 0xBCE670 + parseInt(base) - 0xBC0000 ), / / { / / onEnter: function (args) { / / console.log(Process.getCurrentThreadId(), "encn " / / , this.context.ecx / / , this.context.ebp / / , this.context.edi / / , this.context.esi / / , this.context.esp.add( 0x4 ).readPointer() / / , "caller =" , this.context.esp.readPointer().sub(base) / / ) / / console.log() / / / / seeHexA(this.context.esp) / / console.log(Process.getCurrentThreadId(), "------------------------" ) / / } / / } / / ) / / Interceptor.attach(ptr( 0xBC9B90 + parseInt(base) - 0xBC0000 ), / / { / / onEnter: function (args) { / / console.log(this.context.ecx, this.context.esp.add( 4 ).readPointer(), this.context.esp.add( 8 ).readPointer(), "caller =" , this.context.esp.readPointer().sub(base)) / / / / console. log ( ' Context : ' + JSON. stringify (this. context)); / / / / console.log( "newstring:" ,this.context.esp.add( 4 ).readPointer().readUtf8String()) / / console.log( "newstring:" ) / / seeHexA(this.context.esp.add( 4 ).readPointer(), parseInt(this.context.esp.add( 8 ).readPointer())) / / / / seeHexA(this.context.esp) / / console.log( "------------------------" ) / / } / / }) / / Interceptor.attach(ptr( 0x40A4B0 + parseInt(base) - 0x00400000 ), / / { / / onEnter: function (args) { / / console.log(this.context.ecx, this.context.esp.add( 4 ).readPointer(), this.context.esp.add( 8 ).readPointer(), "caller =" , this.context.esp.readPointer().sub(base)) / / / / console. log ( ' Context : ' + JSON. stringify (this. context)); / / / / console.log( "newstring:" ,this.context.esp.add( 4 ).readPointer().readUtf8String()) / / console.log( "newstring2:" ) / / seeHexA(this.context.esp.add( 4 ).readPointer(), parseInt(this.context.esp.add( 8 ).readPointer())) / / / / seeHexA(this.context.esp) / / console.log( "------------------------" ) / / } / / }) Interceptor.attach(ptr( 0xBCDA90 + parseInt(base) - 0xBC0000 ), { onEnter: function (args) { this.arg0 = this.context.edx this.arg1 = this.context.ecx console.log(Process.getCurrentThreadId(), this.context.edx, this.context.ecx, this.context.esp.add( 4 ).readPointer(), "caller =" , this.context.esp.readPointer().sub(base)) / / console. log ( ' Context : ' + JSON. stringify (this. context)); console.log(Process.getCurrentThreadId(), "getcodedat:" , this.context.esp.add( 4 ).readPointer().readUtf8String()) console.log() } } ) Interceptor.attach(ptr( 0x00C0E460 + parseInt(base) - 0xBC0000 ), { onEnter: function (args) { console.log(Process.getCurrentThreadId(), "uc_mem_write:" , this.context.esp.add( 4 ).readPointer(), this.context.esp.add( 8 ).readPointer(), this.context.esp.add( 0xc ).readPointer(), this.context.esp.add( 0x10 ).readPointer(), this.context.esp.add( 0x14 ).readPointer(), "caller =" , this.context.esp.readPointer().sub(base)) / / console. log ( ' Context : ' + JSON. stringify (this. context)); seeHexA(this.context.esp.add( 0x10 ).readPointer(), this.context.esp.add( 0x14 ).readPointer()) console.log(Process.getCurrentThreadId(), "------------------------" ) } } ) Interceptor.attach(ptr( 0x00C0E1F0 + parseInt(base) - 0xBC0000 ), { onEnter: function (args) { this.dst = this.context.esp.add( 0x10 ).readPointer() this.size = this.context.esp.add( 0x14 ).readPointer() console.log(Process.getCurrentThreadId(), "uc_mem_read:" , this.context.esp.add( 4 ).readPointer(), this.context.esp.add( 8 ).readPointer(), this.context.esp.add( 0xc ).readPointer(), this.context.esp.add( 0x10 ).readPointer(), this.context.esp.add( 0x14 ).readPointer(), "caller =" , this.context.esp.readPointer().sub(base)) / / console. log ( ' Context : ' + JSON. stringify (this. context)); console.log() } , onLeave: function (retval) { console.log(Process.getCurrentThreadId(), "uc_mem_read->" , retval) seeHexA(this.dst, this.size) / / this.dst.writeU32( 1 ) / / this.dst.add( 0x18 ).writeU32( 1 ) seeHexA(this.dst, this.size) console.log(Process.getCurrentThreadId(), "------------------------" ) } } ) / / if ( hash ( input ) = = "6749dae311865d64db83d5ae75bac3c9e36b3aa6f24caba655d9682f7f071023" ){} |
两次write,分别是代码和输入数据的hash。获得代码如下
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 | ROM: 000436AC ADR R0, a182b32359558eb ; "182b32359558eb092511b7166867503ddd83fbe" ... ROM: 000436B0 NOP ROM: 000436B4 PUSH {R0} ROM: 000436B8 ADR R0, aFe5f0fc640cbbc ; "fe5f0fc640cbbc113406f042d08cc60ba784c77" ... ROM: 000436BC NOP ROM: 000436C0 PUSH {R0} ROM: 000436C4 ADR R0, a4fc82b26aecb47 ; "4fc82b26aecb47d2868c4efbe3581732a3e7cbc" ... ROM: 000436C8 NOP ROM: 000436CC PUSH {R0} ROM: 000436D0 ADR R1, aFlags ; "flags:" ROM: 000436D4 NOP ROM: 000436D8 MOV R1, #0xC ROM: 000436DC ROM: 000436DC loc_436DC ; CODE XREF: sub_4363C + E0↓j ROM: 000436DC MOV R0, #0x4033 ROM: 000436E4 POP {R2} ROM: 000436E8 MOV R8, R2 ROM: 000436EC MOV R5, #0 ROM: 000436F0 SUB R1, R1, #1 ROM: 000436F4 ROM: 000436F4 loc_436F4 ; CODE XREF: sub_4363C + D8↓j ROM: 000436F4 LDR R3, [R0] ROM: 000436F8 LDR R4, [R2] ROM: 000436FC ADD R5, R5, #1 ROM: 00043700 CMP R5, #0x10 ROM: 00043704 BGE loc_43724 ROM: 00043708 ADD R0, R0, #4 ROM: 0004370C ADD R2, R2, #4 ROM: 00043710 CMP R3, R4 ROM: 00043714 BEQ loc_436F4 ROM: 00043718 CMP R1, #0 ROM: 0004371C BNE loc_436DC ROM: 00043720 B loc_43750 ROM: 00043724 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ROM: 00043724 ROM: 00043724 loc_43724 ; CODE XREF: sub_4363C + C8↑j ROM: 00043724 ADR R9, a6749dae311865d ; "6749dae311865d64db83d5ae75bac3c9e36b3aa" ... ROM: 00043728 NOP ROM: 0004372C CMP R8, R9 ROM: 00043730 BNE loc_43750 ROM: 00043734 MOV R1, #0x14390 ROM: 00043744 MOV R2, #1 ROM: 00043748 STR R2, [R1] ROM: 0004374C STR R2, [R1, #0x18] |
将一堆字符串push,和输入比较,如果相等且为"6749dae311865d64db83d5ae75bac3c9e36b3aa"...这一项则验证成功。
但是输入经过了hash处理,
这个代码现在真是越看越怪,尤其是这个flag:
2.调试确定逻辑
为了确认输入处理,还是需要调试下,从崩溃时的栈上获取信息,最终定位到
1 2 3 4 5 6 7 8 | v21 = CreateTimerQueue(); this[ 165 ] = v21; if ( v21 ) { CreateTimerQueueTimer((PHANDLE)this + 166 , v21, _IsNonwritableInCurrentImage, (PVOID)this[ 7 ], 0x1F4u , 0x7D0u , 0 ); CreateTimerQueueTimer((PHANDLE)this + 167 , (HANDLE)this[ 165 ], Callback, 0 , 0x258u , 0x7D0u , 0 ); CreateTimerQueueTimer((PHANDLE)this + 168 , (HANDLE)this[ 165 ], sub_6E79D0, 0 , 0x2BCu , 0x7D0u , 0 ); CreateTimerQueueTimer((PHANDLE)this + 169 , (HANDLE)this[ 165 ], sub_6E7A60, 0 , 0x320u , 0x7D0u , 0 ) |
把这个干掉之后运行正常。
跟踪输入逻辑,可以发现先经过sha256,然后rsa加密,使用时再rsa解密。
看了下代码解密逻辑,确认没什么思路,唯一的条件就是
1 2 | sha256( input ) = = "6749dae311865d64db83d5ae75bac3c9e36b3aa6f24caba655d9682f7f071023" len ( input ) = = 32 |
3.利用提示
前面的flags:没什么用,明显是个提示,并且这段代码里多了很多无用的字符串。
将其分割成两半试试
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 | import hashlib s = [ "e0bc614e4fd035a488619799853b0751" , "43deea596c477b8dc077e309c0fe42e9" , "6b86b273ff34fce19d6b804eff5a3f57" , "47ada4eaa22f1d49c01e52ddb7875b4b" , "d8bdf9a0cb27a193a1127de2924b6e5a" , "9e4c2d3b3fe42e935e160c011f3df1fc" , "d4735e3a265e16eee03f59718b9b5d03" , "019c07d8b6c51f90da3a666eec13ab35" , "6749dae311865d64db83d5ae75bac3c9" , "e36b3aa6f24caba655d9682f7f071023" , "ea96b41c1f9365c2c9e6342f5faaeab2" , "a44471efe1e65a2356a974646d2588fd" , "5b65712d565c1551340998102d418cec" , "cb35db8dbfb45f9041c4cae483d8717b" , "4e07408562bedb8b60ce05c1decfe3ad" , "16b72230967de01f640b7e4729b49fce" , "033c339a7975542785be7423a5b32fa8" , "047813689726214143cdd7939747709c" , "4b227777d4dd1fc61c6f884f48641d02" , "b4d121d3fd328cb08b5531fcacdabf8a" , "c81d40dbeed369f1476086cf882dd36b" , "f1c3dc35e07006f0bec588b983055487" , "ef2d127de37b942baad06145e54b0c61" , "9a1f22327b2ebbcfbec78f5564afe39d" , "9e259b7f6b4c741937a96a9617b3e6b8" , "4e166ff6e925e414e7b72936f5a2a51f" , "e7f6c011776e8db7cd330b54174fd76f" , "7d0216b612387a5ffcfb81e6f0919683" , "1048f03db5d45f654b955eae20d84b72" , "673680fb13b318e7da22e8dce58df21c" , "7902699be42c8a8e46fbbb4501726517" , "e86b22c56a189f7625a6da49081b2451" , "8f0703d406fdb0ea8011d5de342c3aca" , "62214758a8a2b5b8a4e9f1c8c6c42462" , "2c624232cdd221771294dfbb310aca00" , "0a0df6ac8b66b696d90ef06fdefb64a3" , "182b32359558eb092511b7166867503d" , "dd83fbe5b42f2545e1903016e721393d" , "19581e27de7ced00ff1ce50b2047e7a5" , "67c76b1cbaebabe5ef03f7c3017bb5b7" , "fe5f0fc640cbbc113406f042d08cc60b" , "a784c775f7c3299985665323c5fbcdc4" , "4a44dc15364204a80fe80e9039455cc1" , "608281820fe2b24f1e5233ade6af1dd5" , "4fc82b26aecb47d2868c4efbe3581732" , "a3e7cbcc6c2efb32062c08170a05eeb8" , "1ba586c0b89202f7307b61f122933097" , "8a843afc98589ffc6a62f209225d3528" , ] for x in s: for x in s: print (x, len (x), hashlib.sha256(x.encode()).hexdigest()) |
总结
全是套路,好!
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界
最后于 2023-9-7 12:21
被kanxue编辑
,原因:
赞赏
他的文章
看原图