首页
社区
课程
招聘
[原创]KCTF 2023 第三题 解题过程
2023-9-6 22:44 8502

[原创]KCTF 2023 第三题 解题过程

2023-9-6 22:44
8502

以为又是个算法题,没想到是个逆向加脑洞题

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())

总结

全是套路,好!


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2023-9-7 12:21 被kanxue编辑 ,原因:
收藏
免费 1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回