以为又是个算法题,没想到是个逆向加脑洞题
字符串特征 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:000436ACADR             R0, a182b32359558eb ; "182b32359558eb092511b7166867503ddd83fbe"...ROM:000436B0NOPROM:000436B4PUSH            {R0}ROM:000436B8ADR             R0, aFe5f0fc640cbbc ; "fe5f0fc640cbbc113406f042d08cc60ba784c77"...ROM:000436BCNOPROM:000436C0PUSH            {R0}ROM:000436C4ADR             R0, a4fc82b26aecb47 ; "4fc82b26aecb47d2868c4efbe3581732a3e7cbc"...ROM:000436C8NOPROM:000436CCPUSH            {R0}ROM:000436D0ADR             R1, aFlags ; "flags:"ROM:000436D4NOPROM:000436D8MOV             R1, #0xCROM:000436DCROM:000436DCloc_436DC                               ; CODE XREF: sub_4363C+E0↓jROM:000436DCMOV             R0, #0x4033ROM:000436E4POP             {R2}ROM:000436E8MOV             R8, R2ROM:000436ECMOV             R5, #0ROM:000436F0SUB             R1, R1, #1ROM:000436F4ROM:000436F4loc_436F4                               ; CODE XREF: sub_4363C+D8↓jROM:000436F4LDR             R3, [R0]ROM:000436F8LDR             R4, [R2]ROM:000436FCADD             R5, R5, #1ROM:00043700CMPR5, #0x10ROM:00043704BGE             loc_43724ROM:00043708ADD             R0, R0, #4ROM:0004370CADD             R2, R2, #4ROM:00043710CMPR3, R4ROM:00043714BEQ             loc_436F4ROM:00043718CMPR1, #0ROM:0004371CBNE             loc_436DCROM:00043720B               loc_43750ROM:00043724; ---------------------------------------------------------------------------ROM:00043724ROM:00043724loc_43724                               ; CODE XREF: sub_4363C+C8↑jROM:00043724ADR             R9, a6749dae311865d ; "6749dae311865d64db83d5ae75bac3c9e36b3aa"...ROM:00043728NOPROM:0004372CCMPR8, R9ROM:00043730BNE             loc_43750ROM:00043734MOV             R1, #0x14390ROM:00043744MOV             R2, #1ROM:00043748STRR2, [R1]ROM:0004374CSTRR2, [R1,#0x18] | 
将一堆字符串push,和输入比较,如果相等且为"6749dae311865d64db83d5ae75bac3c9e36b3aa"...这一项则验证成功。
但是输入经过了hash处理,
这个代码现在真是越看越怪,尤其是这个flag:
为了确认输入处理,还是需要调试下,从崩溃时的栈上获取信息,最终定位到
| 
      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 | 
前面的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
      | importhashlibs=[    "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",]forx ins:    forx ins:print(x, len(x), hashlib.sha256(x.encode()).hexdigest()) | 
全是套路,好!
更多【KCTF 2023 第三题 解题过程】相关视频教程:www.yxfzedu.com