逆向笔记

逆向的开始!从这开始记录我在做逆向题目时遇到的问题以及总结一些逆向的知识点

BFS脚本:

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
#include<iostream>
#include<queue>
using namespace std;
char map[15][13]={
1,1,1,1,1,1,1,1,1,1,1,1,1,
1,3,0,0,1,1,1,1,1,1,0,0,0,
1,1,1,0,1,1,1,1,1,0,0,1,0,
1,0,0,0,1,1,1,1,1,0,1,1,0,
1,0,1,1,1,1,1,1,1,0,1,1,0,
1,0,1,1,1,1,1,1,1,0,1,0,0,
1,0,0,0,0,0,1,1,1,0,1,0,1,
1,1,1,1,1,0,1,1,1,0,1,0,1,
1,1,1,1,1,0,1,1,1,0,1,0,0,
1,1,1,0,0,0,1,1,0,0,1,1,0,
1,1,1,0,1,1,1,1,0,1,1,0,0,
1,0,0,0,1,1,0,0,0,1,1,0,1,
1,0,1,1,1,1,0,1,1,1,1,0,1,
1,0,0,0,0,0,0,1,1,1,1,0,4,
1,1,1,1,1,1,1,1,1,1,1,1,1};

struct Point
{
int x;
int y;
string path;
char mp[15][13]={0};//标记路线
};

char w1[4] = {0,1,0,-1};
char w2[4] = {1,0,-1,0};
char pa[5] = "sdwa";
queue<Point>q;

int main()
{

Point first = {1,1,""};
first.mp[first.y][first.x]=1;
q.push(first);
while(!q.empty())
{
Point now = q.front();
q.pop();
for(int i=0;i<4;i++)
{
Point temp = now;
temp.x+=w1[i];
temp.y+=w2[i];
temp.path+=pa[i];
if(map[temp.y][temp.x]==4)
{
cout<<temp.path<<endl;
}
if(map[temp.y][temp.x]==0&&temp.x>=0&&temp.x<13&&temp.y>=0&&temp.y<15&&temp.mp[temp.y][temp.x]==0)
{
temp.mp[temp.y][temp.x]=1;
q.push(temp);
}
}
}
}

Frida环境搭建与测试:

进入开发者模式,与手机进行连接adb connect 127.0.0.1:62001(PC端命令行)

查看是否连接成功 adb devices

frida-server处理器类型与Android处理器对齐

查询虚拟机处理器架构: adb shell getprop ro.product.cpu.abi

进入frida移动端目录 adb shell

cd /data/local/tmp

运行 ./frida-server

执行 frida -U -f MyApplication 进行连接\

显示找不到MyApplication,查询app名称的id frida-ps -U -a

结果:

image-20231022105344293

再次执行 frida -U -f com.example.myapplication

连接成功,脚本测试

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
import frida
import sys

def on_message(message, data):
if message['type'] == 'send':
print("*****[frida hook]***** : {0}".format(message['payload']))
else:
print("*****[frida hook]***** : " + str(message))

#js脚本
jscode = """
Java.perform(function () {
var MainActivity = Java.use('com.example.myapplication.MainActivity');
MainActivity.print.implementation = function(){
send("Hook Start");
};
});
"""

process = frida.get_usb_device().attach('MyApplication')#应用程序名称,和包名不一样
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()

RC4解密脚本:

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
import base64
from Crypto.Util.number import long_to_bytes

def rc4_setup(key):
if isinstance(key, str):
key = key.encode()

S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]

return S


def rc4_crypt(data, key):#文本解密
if isinstance(data, str):
data = data.encode()

S = rc4_setup(key)
i, j = 0, 0
res = []
for byte in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
res.append(byte ^ S[(S[i] + S[j]) % 256])

return bytes(res)

def rc4_decrypt(key_hex, data_hex):#十六进制解密
key = bytes.fromhex(key_hex)
data = bytes.fromhex(data_hex)
res = rc4_crypt(data, key)
return res.hex()



if __name__ == '__main__':
enc = b'FUZAza8WP5FERi17FvdHowYFSNs6cOk1h0tQLSqk'
key = b'BirkenwaldCTF{This_is_f4ke_f1ag}'
enc = base64.b64decode(enc)
enc = rc4_decrypt(key.hex(),enc.hex())
enc = long_to_bytes(int(enc,16)).decode()
print(enc)



&0xff在python中可以表示取无符号整数,0xff表示八个比特1,也就是数byte数进行无符号运算,&0x7f表示取有符号字节类型

RC4的c语言加密脚本:

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma warning(disable:4996)

unsigned char data[26] = { 0x4e,0xd5,0xc,0xed,0x4d,0xbf,0x9e,0xe8,0x43,0x50,0x8,0xd0,0xa0,0xe2,0x4b,0x62,0xbe,0xab,0x0,0x22,0xb3,0xd8,0x68,0x3f,0x80,0xa4 };

// 初始化 S 表,根据密钥打乱 S 表
void init(unsigned char* S, const unsigned char* key, int keylen) {
for (int i = 0; i < 256; i++) {
S[i] = i;
}
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + S[i] + key[i % keylen]) % 256;
// 交换 S[i] 和 S[j]
unsigned char temp = S[i];
S[i] = S[j];
S[j] = temp;
}
}

// 生成一个字节的密钥流
unsigned char generate(unsigned char* S, int* i, int* j) {
*i = (*i + 1) % 256;
*j = (*j + S[*i]) % 256;
// 交换 S[i] 和 S[j]
unsigned char temp = S[*i];
S[*i] = S[*j];
S[*j] = temp;
// 返回 S[S[i] + S[j]]
return S[(S[*i] + S[*j]) % 256];
}

// 对数据进行加密或解密
void encrypt(unsigned char* data, int datalen, const unsigned char* key, int keylen) {
unsigned char S[256]; // 256 字节的数据表
int i = 0, j = 0; // 两个索引变量
// 初始化 S 表
init(S, key, keylen);
for (int k = 0; k < datalen; k++) {
// 数据和密钥流进行异或运算
data[k] ^= generate(S, &i, &j);
}
}

// 测试函数
int main() {
// 假设这是要加密或解密的数据
unsigned char input[26];
printf("Do you know flag?\n");
printf("Please tell me:\n");
scanf("%s", input);
//unsigned char data[] = "CynopsCTF{RC4_1s_4w3s0m3!}";
int datalen = 26;
// 假设这是加密或解密的密钥
unsigned char key[] = "CynopsCTF{This_is_f4ke_f1ag}";
int keylen = strlen((char*)key);
// 对数据进行加密
encrypt(input, datalen, key, keylen);
for (int i = 0; i < 26; i++)
{
if (input[i] != data[i])
goto label;
}
printf("You're so smart. This's what I want!!!\n");
system("pause");
exit(0);
label:
printf("Oh,You don't seem to know what I want\n");
printf("Please leave here\n");
system("pause");
exit(0);

return 0;
}

AES加解密C脚本

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"

/**
* S盒
*/
static const int S[16][16] = { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

/**
* 逆S盒
*/
static const int S2[16][16] = { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };

/**
* 获取整形数据的低8位的左4个位
*/
static int getLeft4Bit(int num) {
int left = num & 0x000000f0;
return left >> 4;
}

/**
* 获取整形数据的低8位的右4个位
*/
static int getRight4Bit(int num) {
return num & 0x0000000f;
}
/**
* 根据索引,从S盒中获得元素
*/
static int getNumFromSBox(int index) {
int row = getLeft4Bit(index);
int col = getRight4Bit(index);
return S[row][col];
}

/**
* 把一个字符转变成整型
*/
static int getIntFromChar(char c) {
int result = (int)c;
return result & 0x000000ff;
}

/**
* 把16个字符转变成4X4的数组,
* 该矩阵中字节的排列顺序为从上到下,
* 从左到右依次排列。
*/
static void convertToIntArray(char* str, int pa[4][4]) {
int k = 0, i = 0, j = 0;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) {
pa[j][i] = getIntFromChar(str[k]);
k++;
}
}

/**
* 打印4X4的数组
*/
static void printArray(int a[4][4]) {
int i, j;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++)
printf("a[%d][%d] = 0x%x ", i, j, a[i][j]);
printf("\n");
}
printf("\n");
}

/**
* 打印字符串的ASSCI,
* 以十六进制显示。
*/
static void printASSCI(char* str, int len) {
int i;
for (i = 0; i < len; i++)
printf("0x%x ", getIntFromChar(str[i]));
printf("\n");
}

/**
* 把连续的4个字符合并成一个4字节的整型
*/
static int getWordFromStr(char* str)
{
int one = getIntFromChar(str[0]);
one = one << 24;
int two = getIntFromChar(str[1]);
two = two << 16;
int three = getIntFromChar(str[2]);
three = three << 8;
int four = getIntFromChar(str[3]);
return one | two | three | four;
}

/**
* 把一个4字节的数的第一、二、三、四个字节取出,
* 入进一个4个元素的整型数组里面。
*/
static void splitIntToArray(int num, int array[4]) {
int one = num >> 24;
array[0] = one & 0x000000ff;
int two = num >> 16;
array[1] = two & 0x000000ff;
int three = num >> 8;
array[2] = three & 0x000000ff;
array[3] = num & 0x000000ff;
}

/**
* 将数组中的元素循环左移step位
*/
static void leftLoop4int(int array[4], int step) {
int temp[4];
int i;
for (i = 0; i < 4; i++)
temp[i] = array[i];

int index = step % 4 == 0 ? 0 : step % 4;
for (i = 0; i < 4; i++) {
array[i] = temp[index];
index++;
index = index % 4;
}
}

/**
* 把数组中的第一、二、三和四元素分别作为
* 4字节整型的第一、二、三和四字节,合并成一个4字节整型
*/
static int mergeArrayToInt(int array[4]) {
int one = array[0] << 24;
int two = array[1] << 16;
int three = array[2] << 8;
int four = array[3];
return one | two | three | four;
}

/**
* 常量轮值表
*/
static const int Rcon[10] = { 0x01000000, 0x02000000,
0x04000000, 0x08000000,
0x10000000, 0x20000000,
0x40000000, 0x80000000,
0x1b000000, 0x36000000 };
/**
* 密钥扩展中的T函数
*/
static int T(int num, int round) {
int numArray[4];
splitIntToArray(num, numArray);
leftLoop4int(numArray, 1);//字循环
int i;

//字节代换
for (i = 0; i < 4; i++)
numArray[i] = getNumFromSBox(numArray[i]);

int result = mergeArrayToInt(numArray);
return result ^ Rcon[round];
}

//密钥对应的扩展数组
static int w[44];

/**
* 扩展密钥,结果是把w[44]中的每个元素初始化
*/
static void extendKey(char* key)
{
int i, j;
for (i = 0; i < 4; i++)
w[i] = getWordFromStr(key + i * 4);

for (i = 4, j = 0; i < 44; i++)
{
if (i % 4 == 0)
{
w[i] = w[i - 4] ^ T(w[i - 1], j);
j++;//下一轮
}
else
w[i] = w[i - 4] ^ w[i - 1];
}
}

/**
* 轮密钥加
*/
static void addRoundKey(int array[4][4], int round) {
int warray[4];
int i, j;
for (i = 0; i < 4; i++) {

splitIntToArray(w[round * 4 + i], warray);

for (j = 0; j < 4; j++) {
array[j][i] = array[j][i] ^ warray[j];
}
}
}

/**
* 字节代换
*/
static void subBytes(int array[4][4]) {
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
array[i][j] = getNumFromSBox(array[i][j]);
}

/**
* 行移位
*/
static void shiftRows(int array[4][4]) {
int rowTwo[4], rowThree[4], rowFour[4];
//复制状态矩阵的第2,3,4行
int i;

for (i = 0; i < 4; i++) {
rowTwo[i] = array[1][i];
rowThree[i] = array[2][i];
rowFour[i] = array[3][i];
}
//循环左移相应的位数
leftLoop4int(rowTwo, 1);
leftLoop4int(rowThree, 2);
leftLoop4int(rowFour, 3);

//把左移后的行复制回状态矩阵中
for (i = 0; i < 4; i++) {
array[1][i] = rowTwo[i];
array[2][i] = rowThree[i];
array[3][i] = rowFour[i];
}
}

/**
* 列混合要用到的矩阵
*/
static const int colM[4][4] = { 2, 3, 1, 1,
1, 2, 3, 1,
1, 1, 2, 3,
3, 1, 1, 2 };

static int GFMul2(int s) {
int result = s << 1;
int a7 = result & 0x00000100;

if (a7 != 0) {
result = result & 0x000000ff;
result = result ^ 0x1b;
}

return result;
}

static int GFMul3(int s) {
return GFMul2(s) ^ s;
}

static int GFMul4(int s) {
return GFMul2(GFMul2(s));
}

static int GFMul8(int s) {
return GFMul2(GFMul4(s));
}

static int GFMul9(int s) {
return GFMul8(s) ^ s;
}

static int GFMul11(int s) {
return GFMul9(s) ^ GFMul2(s);
}

static int GFMul12(int s) {
return GFMul8(s) ^ GFMul4(s);
}

static int GFMul13(int s) {
return GFMul12(s) ^ s;
}

static int GFMul14(int s) {
return GFMul12(s) ^ GFMul2(s);
}

/**
* GF上的二元运算
*/
static int GFMul(int n, int s) {
int result;

if (n == 1)
result = s;
else if (n == 2)
result = GFMul2(s);
else if (n == 3)
result = GFMul3(s);
else if (n == 0x9)
result = GFMul9(s);
else if (n == 0xb)//11
result = GFMul11(s);
else if (n == 0xd)//13
result = GFMul13(s);
else if (n == 0xe)//14
result = GFMul14(s);

return result;
}
/**
* 列混合
*/
static void mixColumns(int array[4][4]) {

int tempArray[4][4];
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
tempArray[i][j] = array[i][j];

for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) {
array[i][j] = GFMul(colM[i][0], tempArray[0][j]) ^ GFMul(colM[i][1], tempArray[1][j])
^ GFMul(colM[i][2], tempArray[2][j]) ^ GFMul(colM[i][3], tempArray[3][j]);
}
}
/**
* 把4X4数组转回字符串
*/
static void convertArrayToStr(int array[4][4], char* str) {
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
*str++ = (char)array[j][i];
}
/**
* 检查密钥长度
*/
static int checkKeyLen(int len) {
if (len == 16)
return 1;
else
return 0;
}

/**
* 参数 p: 明文的字符串数组。
* 参数 plen: 明文的长度。
* 参数 key: 密钥的字符串数组。
*/
void aes(char* p, int plen, char* key) {

int keylen = strlen(key);

if (!checkKeyLen(keylen)) {
printf("keylen erro;keylen=%d\n", keylen);
exit(0);
}

extendKey(key);//扩展密钥
int pArray[4][4];
int i, k;
for (k = 0; k < plen; k += 16) {
convertToIntArray(p + k, pArray);

addRoundKey(pArray, 0);//一开始的轮密钥加

for (i = 1; i < 10; i++) {//前9轮

subBytes(pArray);//字节代换

shiftRows(pArray);//行移位

mixColumns(pArray);//列混合

addRoundKey(pArray, i);
}

//第10轮
subBytes(pArray);//字节代换

shiftRows(pArray);//行移位

addRoundKey(pArray, 10);

convertArrayToStr(pArray, p + k);
}
}
/**
* 根据索引从逆S盒中获取值
*/
static int getNumFromS1Box(int index) {
int row = getLeft4Bit(index);
int col = getRight4Bit(index);
return S2[row][col];
}
/**
* 逆字节变换
*/
static void deSubBytes(int array[4][4]) {
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
array[i][j] = getNumFromS1Box(array[i][j]);
}
/**
* 把4个元素的数组循环右移step位
*/
static void rightLoop4int(int array[4], int step) {
int temp[4];
int i;
for (i = 0; i < 4; i++)
temp[i] = array[i];

int index = step % 4 == 0 ? 0 : step % 4;
index = 3 - index;
for (i = 3; i >= 0; i--) {
array[i] = temp[index];
index--;
index = index == -1 ? 3 : index;
}
}

/**
* 逆行移位
*/
static void deShiftRows(int array[4][4]) {
int rowTwo[4], rowThree[4], rowFour[4];
int i;
for (i = 0; i < 4; i++) {
rowTwo[i] = array[1][i];
rowThree[i] = array[2][i];
rowFour[i] = array[3][i];
}

rightLoop4int(rowTwo, 1);
rightLoop4int(rowThree, 2);
rightLoop4int(rowFour, 3);

for (i = 0; i < 4; i++) {
array[1][i] = rowTwo[i];
array[2][i] = rowThree[i];
array[3][i] = rowFour[i];
}
}
/**
* 逆列混合用到的矩阵
*/
static const int deColM[4][4] = { 0xe, 0xb, 0xd, 0x9,
0x9, 0xe, 0xb, 0xd,
0xd, 0x9, 0xe, 0xb,
0xb, 0xd, 0x9, 0xe };

/**
* 逆列混合
*/
static void deMixColumns(int array[4][4]) {
int tempArray[4][4];
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
tempArray[i][j] = array[i][j];

for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) {
array[i][j] = GFMul(deColM[i][0], tempArray[0][j]) ^ GFMul(deColM[i][1], tempArray[1][j])
^ GFMul(deColM[i][2], tempArray[2][j]) ^ GFMul(deColM[i][3], tempArray[3][j]);
}
}
/**
* 把两个4X4数组进行异或
*/
static void addRoundTowArray(int aArray[4][4], int bArray[4][4]) {
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
aArray[i][j] = aArray[i][j] ^ bArray[i][j];
}
/**
* 从4个32位的密钥字中获得4X4数组,
* 用于进行逆列混合
*/
static void getArrayFrom4W(int i, int array[4][4]) {
int index = i * 4;
int colOne[4], colTwo[4], colThree[4], colFour[4];
splitIntToArray(w[index], colOne);
splitIntToArray(w[index + 1], colTwo);
splitIntToArray(w[index + 2], colThree);
splitIntToArray(w[index + 3], colFour);

for (i = 0; i < 4; i++) {
array[i][0] = colOne[i];
array[i][1] = colTwo[i];
array[i][2] = colThree[i];
array[i][3] = colFour[i];
}
}

/**
* 参数 c: 密文的字符串数组。
* 参数 clen: 密文的长度。
* 参数 key: 密钥的字符串数组。
*/
void deAes(char* c, int clen, char* key) {

int keylen = strlen(key);
if (clen == 0 || clen % 16 != 0) {
printf("Ciphertext characters must be a multiple of 16! Now the length is zero:%d\n", clen);
exit(0);
}

if (!checkKeyLen(keylen)) {
printf("Key character length error! The lengths must be 16, 24, and 32. The current length is:%d\n", keylen);
exit(0);
}

extendKey(key);//扩展密钥
int cArray[4][4];
int i, k;
for (k = 0; k < clen; k += 16) {
convertToIntArray(c + k, cArray);


addRoundKey(cArray, 10);

int wArray[4][4];
for (i = 9; i >= 1; i--) {
deSubBytes(cArray);

deShiftRows(cArray);

deMixColumns(cArray);
getArrayFrom4W(i, wArray);
deMixColumns(wArray);

addRoundTowArray(cArray, wArray);
}

deSubBytes(cArray);

deShiftRows(cArray);

addRoundKey(cArray, 0);

convertArrayToStr(cArray, c + k);

}
}

.rodata段是用来存放只读数据的一块内存区域,例如常量数据。ro代表read only,即只读的意思。.rodata段也叫常量区,它属于静态内存分配。使用const修饰符,例如const int a = 10;,这样的变量会被放在.rodata段中

Ida Python脚本:

1
2
3
4
5
6
7
start = 0x00401500
end = start + 0xBA
xor = 0x41
for i in range(start,end):
data = get_wide_byte(i)
patch_byte(i,data^xor)
print("{} patched".format(i))

attribute实现main函数之前执行相应函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

void before() __attribute__((constructor));
void after() __attribute__((destructor));

void before() {
printf("this is function %s\n",__func__);
return;
}

void after(){
printf("this is function %s\n",__func__);
return;
}

int main(){
printf("this is function %s\n",__func__);
return 0;
}

函数调用默认传参的几种方式

$$
cdecl:这是C语言默认的调用约定,它要求参数从右向左依次压入栈中,由调用者负责清理栈空间。这种方式适用于可变数量的参数,如printf函数。在Windows和Linux系统中,cdecl通常用于32位x86架构的编译器。
$$

$$
stdcall:这是Windows API默认的调用约定,它要求参数从右向左依次压入栈中,由被调用者负责清理栈空间。这种方式适用于固定数量的参数,如MessageBox函数。在Windows系统中,stdcall通常用于32位x86架构的编译器。
$$

$$
fastcall:这是一种优化的调用约定,它要求将部分参数通过寄存器传递,而不是全部压入栈中,以提高函数调用的效率。不同的编译器可能会有不同的fastcall实现,例如Microsoft Visual C++和Borland C++ Builder分别使用ECX和EDX两个寄存器传递前两个参数,而GCC则使用EAX、EDX和ECX三个寄存器传递前三个参数。在Windows和Linux系统中,fastcall通常用于32位x86架构的编译器。
$$

$$
thiscall:这是C++类成员函数默认的调用约定,它要求将对象指针(this指针)通过ECX寄存器传递给函数,而其他参数则从右向左依次压入栈中。由于thiscall只适用于类成员函数,因此它通常不需要显式指定。在Windows和Linux系统中,thiscall通常用于32位x86架构的编译器。
$$

$$
syscall:这是Linux内核默认的调用约定,它要求将系统调用号通过EAX寄存器传递给内核,而其他参数则通过EBX、ECX、EDX、ESI、EDI和EBP六个寄存器按顺序传递给内核。如果参数超过六个,则需要通过栈传递。在Linux系统中,syscall通常用于32位x86架构的编译器
$$

$$
在64位x86架构(x86_64或AMD64)的编译器中,通常会有更多的寄存器可供使用,因此参数传递方式也会有所不同。例如,在Windows系统中,x64调用约定要求将前四个整数或指针类型的参数通过RCX、RDX、R8和R9四个寄存器传递,而前四个浮点类型的参数通过XMM0、XMM1、XMM2和XMM3四个寄存器传递;在Linux系统中,System V AMD64 ABI要求将前六个整数或指针类型的参数通过RDI、RSI、RDX、RCX、R8和R9六个寄存器传递,而前八个浮点类型的参数通过XMM0到XMM7八个寄存器传递。
$$

gbk解码转中文,先ida64 convert to string,再用b修饰,解码即可

1
2
str = b"\xD7\xA2\xB2\xE1\xB3\xC9\xB9\xA6\xA3\xA1"
print(str.decode('gbk'))

异常处理和反调试

反调试的几种方式记录

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
#include<iostream>
#include<Windows.h>
char flag[5] = "1234";
void change()
{
strcpy_s(flag, size_t(5), "9999");
}


EXCEPTION_DISPOSITION WINAPI myExceptHandler(
struct _EXCEPTION_RECORD* ExceptionRecord,
PVOID EstablisherFrame,
PCONTEXT pcontext,
PVOID DisapatcherContext
)
{
// 第五种方式,类似第四种方式
if (pcontext->Dr0 != 0 || pcontext->Dr1 != 0 || pcontext->Dr2 != 0 || pcontext->Dr3 != 0)
{
printf("myExceptHandler检测到硬件断点\n");
}
else
{
printf("myExceptHandlerm没有检测到硬件断点\n");
}
pcontext->Eip += 2;//跳过程序异常处,要不然程序一直在异常处不继续运行
change();
return ExceptionContinueExecution;
}


int main()
{
#if 1
//第一种,会被x32dbg,OD反反调试器杀掉
BOOL flag = IsDebuggerPresent();
if (flag)
{
printf("IsDebuggerPresent检测到调试器\n");
}
else
{
printf("IsDebuggerPresent没有检测到调试器\n");
}
//第二种,不会被x32,OD反反调试器杀掉
BOOL IsDebug = 0;
//第一个参数是进程句柄,可以检测远程进程是否被调试
CheckRemoteDebuggerPresent(GetCurrentProcess(), &IsDebug);
if (IsDebug)
{
printf("CheckRemoteDebuggerPresent检测到调试器\n");
}
else
{
printf("CheckRemoteDebuggerPresent没有检测到调试器\n");
}
//
//第三种,不会被x32,OD反反调试器杀掉
//CloseHandle函数会试图关闭指定句柄的进程,
#if 1
__try
{
CloseHandle((HANDLE)0x1234);//指定一个不存在的进程句柄
printf("CloseHandle没有检测到调试器\n");
}
__except(1)
{
printf("CloseHandle检测到调试器\n");
}
#endif
//第四种,硬件断点反调试,只要在动调时设置硬件断点就会被发现
CONTEXT context{ 0 };
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(),&context);
//查找寄存器的值,如果有硬件断点,该寄存器的值会改变
if (context.Dr0 != 0 || context.Dr1 != 0 || context.Dr2 != 0 || context.Dr3 != 0)
{
printf("GetThreadContext检测到硬件断点\n");
}
else
{
printf("GetThreadContext没有检测到硬件断点\n");
}
#endif
//第五种
DWORD sehHandler = (DWORD)myExceptHandler;
//上面可能是添加一个异常处理函数,然后下面让异常处理链表SEH增加一个节点,把这个异常处理函数插进去
_asm
{
push myExceptHandler//fs: [0] 指向的是TIB[Thread information Block]结构中的EXCEPTION_REGISTRATION 结构
mov eax,fs:[0]
push eax
mov fs:[0],esp//让fs:[0]指向一个新的EXCEPTION_REGISTRATION 结构(就像链表插入一个新节点)
}
//设置程序异常处
int a = 1;
a = a / 0;
std::cout << flag;
system("pause");
}

dll的编译和使用

先编译dll文件,新建生成dll项目,创建一个新的Mydll.cpp和Mydll.h

Mydll.cpp

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
#include "pch.h"
#include "Mydll.h"

int add(int a, int b)
{
return a + b;
}

int sub(int a, int b)
{
return a-b;
}

int multi(int a, int b)
{
return a*b;
}

int Div(int a, int b)
{
return a/b;
}

int XXOR(int a,int b)
{
return a ^ b;
}

导出的两种方式的其中第一种方式,头文件导出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#pragma once

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

//第一种导出方式
extern "C" MYDLL_API int add(int a, int b);

// Produce the next value in the sequence.
// Returns true on success and updates current value and index;
// false on overflow, leaves current value and index unchanged.
extern "C" MYDLL_API int sub(int a,int b);

// Get the current value in the sequence.
extern "C" MYDLL_API int multi(int a,int b);

// Get the position of the current value in the sequence.
extern "C" MYDLL_API int Div(int a,int b);

导出的第二种方式,源文件新建一个export.def文件

1
2
3
LIBRARY "Mydll"
EXPORTS
XXOR @ 1

新建一个项目导入生成的dll文件

导入的第一种方式,隐式链接的方式调用dll导出寒素

#pragma comment(lib,"Mydll.lib")//lib地址

头文件main.h

1
2
3
4
5
6
#pragma once
#include<iostream>
#include<Windows.h>
#pragma comment(lib,"Mydll.lib")//lib地址
extern "C" int add(int a, int b);

源文件main.cpp

1
2
3
4
5
6
7
8
#include"main.h"
int main()
{
int sum = add(1, 2);
printf("%d", sum);
system("pause");
return 0;
}

第二种显示链接方式调用导出函数

不需要头文件和lib,源文件main.cpp如下

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<Windows.h>
typedef int (*XXOR)(int a, int b);
int main()
{
HMODULE hModule = LoadLibraryW(L"Mydll.dll");
XXOR Xorr = (XXOR)GetProcAddress(hModule, "XXOR");
printf("%d", Xorr(100, 200));
FreeLibrary(hModule);
system("pause");
return 0;
}

c语言RC4,base64加解密代码

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
void init(unsigned char* S, const unsigned char* key, int keylen) {
for (int i = 0; i < 256; i++) {
S[i] = i;
}
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + S[i] + key[i % keylen]) % 256;
// 交换 S[i] 和 S[j]
unsigned char temp = S[i];
S[i] = S[j];
S[j] = temp;
}
}

// 生成一个字节的密钥流
unsigned char generate(unsigned char* S, int* i, int* j) {
*i = (*i + 1) % 256;
*j = (*j + S[*i]) % 256;
// 交换 S[i] 和 S[j]
unsigned char temp = S[*i];
S[*i] = S[*j];
S[*j] = temp;
// 返回 S[S[i] + S[j]]
return S[(S[*i] + S[*j]) % 256];
}

// 对数据进行加密或解密
void encrypt(unsigned char* data, int datalen, const unsigned char* key, int keylen) {
unsigned char S[256]; // 256 字节的数据表
int i = 0, j = 0; // 两个索引变量
// 初始化 S 表
init(S, key, keylen);
for (int k = 0; k < datalen; k++) {
// 数据和密钥流进行异或运算
data[k] ^= generate(S, &i, &j);
}
}

const char* const base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int base64_decode(const char* base64, unsigned char* bindata)
{
int i, j;
unsigned char k;
unsigned char temp[4];

for (i = 0, j = 0; base64[i] != '\0'; i += 4)
{
memset(temp, 0xFF, sizeof(temp));
for (k = 0; k < 64; k++)
{
if (base64char[k] == base64[i])
temp[0] = k;
}
for (k = 0; k < 64; k++)
{
if (base64char[k] == base64[i + 1])
temp[1] = k;
}
for (k = 0; k < 64; k++)
{
if (base64char[k] == base64[i + 2])
temp[2] = k;
}
for (k = 0; k < 64; k++)
{
if (base64char[k] == base64[i + 3])
temp[3] = k;
}

bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2)) & 0xFC)) |
((unsigned char)((unsigned char)(temp[1] >> 4) & 0x03));
if (base64[i + 2] == '=')
break;

bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4)) & 0xF0)) |
((unsigned char)((unsigned char)(temp[2] >> 2) & 0x0F));
if (base64[i + 3] == '=')
break;

bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6)) & 0xF0)) |
((unsigned char)(temp[3] & 0x3F));
}
return j;
}
char* base64_encode(const unsigned char* bindata, char* base64, int binlength)//输入,输出,输入长度
{
int i, j;
unsigned char current;

for (i = 0, j = 0; i < binlength; i += 3)
{
current = (bindata[i] >> 2);
current &= (unsigned char)0x3F;
base64[j++] = base64char[(int)current];

current = ((unsigned char)(bindata[i] << 4)) & ((unsigned char)0x30);
if (i + 1 >= binlength)
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
base64[j++] = '=';
break;
}
current |= ((unsigned char)(bindata[i + 1] >> 4)) & ((unsigned char)0x0F);
base64[j++] = base64char[(int)current];

current = ((unsigned char)(bindata[i + 1] << 2)) & ((unsigned char)0x3C);
if (i + 2 >= binlength)
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
break;
}
current |= ((unsigned char)(bindata[i + 2] >> 6)) & ((unsigned char)0x03);
base64[j++] = base64char[(int)current];

current = ((unsigned char)bindata[i + 2]) & ((unsigned char)0x3F);
base64[j++] = base64char[(int)current];
}
base64[j] = '\0';
return base64;
}
int main() {
unsigned char Buffer[31];
char enc[41];
unsigned char key[] = "BirkenwaldCTF{This_is_f4ke_f1ag}";//RC5密钥
int keylen = (int)strlen((char*)key);
printf("Do you know flag?\n");
printf("Please tell me:\n");
scanf("%s", Buffer);
int datelen = (int)strlen((char*)Buffer);
encrypt(Buffer, datelen, key, keylen);//RC4加密
encrypt2(Buffer, enc, 30);//base64加密
return 0;
}

lea指令使用,防止遗忘

lea,官方解释Load Effective Address,即装入有效地址的意思,它的操作数就是地址;

常见的几种用法:

1、lea eax,[addr]

就是将表达式addr的值放入eax寄存器,示例如下:

lea eax,[401000h]; 将值401000h写入eax寄存器中

lea指令右边的操作数表示一个精指针,上述指令和mov eax,401000h是等价的

2、lea eax,dword ptr [ebx];将ebx的值赋值给eax

3、lea eax,c;其中c为一个int型的变量,该条语句的意思是把c的地址赋值给eax;

内联汇编实现部分常用函数功能练习

在全局内定义字符串后的print hello world,因为编译器会编译的时候会自动寻址,最简单的一种方法

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
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
char format[] = "%s %s\n";
char hello[] = "hello";
char world[] = "world!";
//_declspec(naked)裸函数标识,没有基本函数框架的函数
void _declspec(naked)print()//打印hello world
{
_asm
{
push ebp
mov ebp,esp
sub esp,0x20
mov eax,offset world
push eax
mov eax,offset hello
push eax
mov eax,offset format
push eax
call printf
pop ebx
pop ebx
pop ebx
mov esp,ebp
pop ebp
retn
}
}

int main(void)
{
_asm
{
call print
}
}

利用局部变量字符串,将字符串push进栈中,通过栈寻址,push传参来实现功能

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
#include<stdio.h>
#include <string.h>
//char format[] = "%s %s";//25,73,20,25,73
//char hello[] = "hello";//68,65,6c,6c,6f
//char world[] = "world!";//77,6f,72,6c,64,21
void _declspec(naked)print()
{
_asm
{
push ebp
mov ebp,esp
push eax
push 0x2164//push 后高位自动补零结束字符串
push 0x6c726f77//注意push顺序和十六进制顺序
push 0x6f
push 0x6c6c6568
push 0x73
push 0x25207325
lea eax,dword ptr ss:[esp+0x10]//ss堆栈段,ds数据段
push eax
lea eax,dword ptr ss:[esp+0xc]//注意偏移量计算
push eax
lea eax,dword ptr ss:[esp+0x08]//用lea将字符串地址传入寄存器
push eax
call printf
add esp,0x24
pop eax
mov esp,ebp
pop ebp
ret
}
}

int main(void)
{
//char format[] = "%s %s";//25,73,20,25,73,0a
//char hello[] = "hello";//68,65,6c,6c,6f
//char world[] = "world!";//77,6f,72,6c,64,21
//printf(format, hello, world);
_asm
{
call print
ret
}
}

利用kernel32.dll或者kernelbase.dll模块寻找loadlibrary和getprocaddr函数地址,以此加载想要的模块和获得想要的函数地址,从而调用想要的函数

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#include<stdio.h>
#include <Windows.h>
char Load[] = "LoadLibraryA";//4c,6f,61,64,4c,69,62,72,61,72,79,41 00 len = 0xc
char Get[] = "GetProcAddress";//47,65,74,50,72,6f,63,41,64,64,72,65,73,73 00 len = 0xe
char hello[] = "hello world!";//68,65,6c,6c,6f,20,77,6f,72,6c,64,21 00 len = 0xc
char Msvc[] = "msvcrt.dll";//6d,73,76,63,72,74,2e,64,6c,6c 00 len = 0xa
char Print[] = "printf";//70,72,69,6e,74,66 00 len = 0x6
void _declspec(naked)MyShell()
{
_asm
{
pushad
mov ebp,esp
push 0
push 0x21646c72//
push 0x6f77206f//
push 0x6c6c6568//hello world
push 0x7373//
push 0x65726464//
push 0x41636f72//
push 0x50746547//GetProcAddress
push 0
push 0x41797261//
push 0x7262694c//
push 0x64616f4c//LoadLibraryA
mov ecx,esp
push ecx//esp传参过去
call Func_print
mov esp,ebp
popad
nop
nop
nop
nop
nop//填充五个字节留给jmp指令,因为call指令有一个相对地址
//可以在OD,xdbg中计算jmp的相对地址


ret

Func_GetModule:
push ebp
mov ebp, esp
sub esp, 0x20
push esi
mov esi,dword ptr fs:[0x30]//PEB结构体地址
mov esi,[esi+0xc]//LDR结构体地址
mov esi,[esi+0x1c]//list
mov esi,[esi]//list 第二项kernel32或者kernelbase
mov esi,[esi+0x8]//dllbase
mov eax,esi
pop esi
mov esp, ebp
pop ebp
ret

Func_GetAddr:
push ebp
mov ebp, esp
sub esp, 0x20
mov edx, [ebp + 0x8]//dllbase
mov esi, [edx + 0x3c]//if_anew
lea esi, [esi + edx]//NT header
mov esi, [esi + 0x78]//导出表RVA
lea esi, [edx + esi]//导出表VA
mov edi, [esi + 0x1c]//EAT RVA
lea edi, [edi + edx]//EAT VA
mov [ebp - 0x4], edi//保存
mov edi, [esi + 0x20]//ENT RVA
lea edi, [edi + edx]//ENT VA
mov [ebp - 0x8], edi//保存
mov edi, [esi + 0x24]//EOT RVA
lea edi, [edi+edx]//EOT VA
mov [ebp-0xc],edi//保存
xor eax,eax
jmp tag_cmpfirst
tag_cmpLoop:
inc eax
tag_cmpfirst:
mov esi,[ebp-0x8]//ENT
mov esi,[esi+eax*4]//RVA
lea esi,[esi+edx]//函数名称字符串地址
mov edi,[ebp+0xc]//
mov ecx,[ebp+0x10]//循环次数,ebp+0x10是传来的参数
repe cmpsb
mov esi,[ebp-0xc]
jne tag_cmpLoop
mov esi,[ebp-0xc]//EOT
xor edi,edi//为了不影响结果清空edi
mov di,[esi+eax*2]//word类型,EAT表索引
mov edx,[ebp-0x4]//EAT
mov esi,[edx+edi*4]//函数地址RVA
mov edx,[ebp+0x8]//dllbase
lea eax,[esi+edx]//最终函数地址
mov esp, ebp
pop ebp
retn 0xc//在栈中弹出三个参数


Func_print:
push ebp
mov ebp,esp
sub esp,0x10
call Func_GetModule//获取kernel模块基址
mov [ebp-0x4],eax//[ebp-0x4]保存模块基址
lea ecx,[ebp + 0xc]//load地址
push 0xc
push ecx
push eax
call Func_GetAddr//三个参数,第一个模块基址,第二个字符串地址,第三个字符串长度 loadlibrarya
mov [ebp-0x8],eax//load地址
push 0xe
lea ecx, [ebp + 0x1c]//Get字符串
push ecx
push [ebp-0x4]//模块基址
call Func_GetAddr//三个参数,第一个模块基址,第二个字符串地址,第三个字符串长度 GetProcAddress
mov [ebp-0xc],eax//get地址
push 0x6c6c
push 0x642e7472
push 0x6376736d
lea ecx,dword ptr ss:[ebp-0x1c]
push ecx
call [ebp-0x8]//调用load函数load Msvc.dll,一个参数,字符串地址
push 0x6674
push 0x6e697270
lea ecx,[ebp-0x24]//printf字符串
push ecx
push eax//msvc.dll基址
call [ebp-0xc]//调用GetProcAddress函数,两个参数,一个msvc.dll基址,一个字符串名称
lea ecx,[ebp+0x2c]//hello world地址
push ecx
call eax//printf地址
mov esp,ebp
pop ebp
ret


}
}

int main(void)
{
/*GetProcAddress();
LoadLibraryA()*/
//6d, 73, 76, 63, 72, 74, 2e, 64, 6c, 6c 00 len = 0xa
// //70,72,69,6e,74,66
MyShell();
system("pause");
/* for (int i = 0; i < strlen(Print); i++)
{
printf("%x,", Print[i]);
}*/

}
/*AT&T写法
asm(
"pusha\n"
"mov %esp, %ebp\n"
"push $0\n"
"push $0x21646c72\n"
"push $0x6f77206f\n"
"push $0x6c6c6568\n" // hello world
"push $0x7373\n"
"push $0x65726464\n"
"push $0x41636f72\n"
"push $0x50746547\n" // GetProcAddress
"push $0\n"
"push $0x41797261\n"
"push $0x7262694c\n"
"push $0x64616f4c\n" // LoadLibraryA
"mov %esp, %ecx\n"
"push %ecx\n" // esp传参过去
"call Func_print\n"
"mov %ebp, %esp\n"
"popa\n"
"nop\n"
"nop\n"
"nop\n"
"nop\n"
"nop\n" // 填充五个字节留给jmp指令,因为call指令有一个相对地址
// 可以在OD,xdbg中计算jmp的相对地址
"mov %ebp, %esp\n"
"pop %ebp\n"
"ret\n"

"Func_GetModule:"
"push %ebp\n"
"mov %esp, %ebp\n"
"sub $0x20, %esp\n"
"push %esi\n"
"mov %fs:0x30, %esi\n" // PEB结构体地址
"mov 0xc(%esi), %esi\n" // LDR结构体地址
"mov 0x1c(%esi), %esi\n" // list
"mov (%esi), %esi\n" // list 第二项kernel32或者kernelbase
"mov 0x8(%esi), %esi\n" // dllbase
"mov %esi, %eax\n"
"pop %esi\n"
"mov %ebp, %esp\n"
"pop %ebp\n"
"ret\n"

"Func_GetAddr:"
"push %ebp\n"
"mov %esp, %ebp\n"
"sub $0x20, %esp\n"
"mov 0x8(%ebp), %edx\n" // dllbase
"mov 0x3c(%edx), %esi\n" // if_anew
"lea (%edx, %esi), %esi\n" // NT header
"mov 0x78(%esi), %esi\n" // 导出表RVA
"lea (%edx, %esi), %esi\n" // 导出表VA
"mov 0x1c(%esi), %edi\n" // EAT RVA
"lea (%edx, %edi), %edi\n" // EAT VA
"mov %edi, -0x4(%ebp)\n" // 保存
"mov 0x20(%esi), %edi\n" // ENT RVA
"lea (%edx, %edi), %edi\n" // ENT VA
"mov %edi, -0x8(%ebp)\n" // 保存
"mov 0x24(%esi), %edi\n" // EOT RVA
"lea (%edx, %edi), %edi\n" // EOT VA
"mov %edi, -0xc(%ebp)\n" // 保存
"xor %eax, %eax\n"
"jmp tag_cmpfirst\n"
"tag_cmpLoop:"
"inc %eax\n"
"tag_cmpfirst:"
"mov -0x8(%ebp), %esi\n" // ENT
"mov (%esi, %eax, 4), %esi\n" // RVA
"lea (%edx, %esi), %esi\n" // 函数名称字符串地址
"mov 0xc(%ebp), %edi\n"
"mov 0x10(%ebp), %ecx\n" // 循环次数,ebp+0x10是传来的参数
"repe cmpsb\n"
"mov -0xc(%ebp), %esi\n"
"jne tag_cmpLoop\n"
"mov -0xc(%ebp), %esi\n" // EOT
"xor %edi, %edi\n" // 为了不影响结果清空edi
"mov (%esi, %eax, 2), %di\n" // word类型,EAT表索引
"mov -0x4(%ebp), %edx\n" // EAT
"mov (%edx, %edi, 4), %esi\n" // 函数地址RVA
"mov 0x8(%ebp), %edx\n" // dllbase
"lea (%edx, %esi), %eax\n" // 最终函数地址
"mov %ebp, %esp\n"
"pop %ebp\n"
"ret $0xc\n" // 在栈中弹出三个参数

"Func_print:"
"push %ebp\n"
"mov %esp, %ebp\n"
"sub $0x10, %esp\n"
"call Func_GetModule\n" // 获取kernel模块基址
"mov %eax, -0x4(%ebp)\n" // 保存模块基址
"lea 0xc(%ebp), %ecx\n" // load地址
"push $0xc\n"
"push %ecx\n"
"push %eax\n"
"call Func_GetAddr\n" // 三个参数,第一个模块基址,第二个字符串地址,第三个字符串长度 loadlibrarya
"mov %eax, -0x8(%ebp)\n" // load地址
"push $0xe\n"
"lea 0x1c(%ebp), %ecx\n" // Get字符串
"push %ecx\n"
"push -0x4(%ebp)\n" // 模块基址
"call Func_GetAddr\n" // 三个参数,第一个模块基址,第二个字符串地址,第三个字符串长度 GetProcAddress
"mov %eax, -0xc(%ebp)\n" // get地址
"push $0x6c6c\n"
"push $0x642e7472\n"
"push $0x6376736d\n"
"lea -0x1c(%ebp), %ecx\n"
"push %ecx\n"
"call -0x8(%ebp)\n" // 调用load函数load Msvc.dll,一个参数,字符串地址
"push $0x6674\n"
"push $0x6e697270\n"
"lea -0x24(%ebp), %ecx\n" // printf字符串
"push %ecx\n"
"push %eax\n" // msvc.dll基址
"call -0xc(%ebp)\n" // 调用GetProcAddress函数,两个参数,一个msvc.dll基址,一个字符串名称
"lea 0x2c(%ebp), %ecx\n" // hello world地址
"push %ecx\n"
"call %eax\n" // printf地址
"mov %ebp, %esp\n"
"pop %ebp\n"
"ret\n"
);
*/

准备了一个表格存储栈信息,ebp2对应的是print函数的栈底,pushadd到ebp2是Myshell函数形成的栈帧

msvc
printf
28 6e
printf 24 66
20 msvc
1c msvc 63
18 64
14 6c
esp 0x10
0xc get_adr
0x8 load_adr
0x4 kernel
ebp2 ebp1
4 eip
8 ecx=esp1
load c 64
10 72
14 41
18 0
get1c 50
20 41
24 65
28 73
hello 2c 6c
30 6f
34 21
38 0
pushadd ad

重温栈结构

今天在准备写一个壳的时候需要手写汇编,把汇编十六进制插入PE文件中,一时对栈结构有点遗忘,在xdbg中调了会温故了一下,在这里记一下,防止下一次遗忘

比方说我们有一个main函数时,在函数开始时经常有如下汇编代码

1
2
3
4
5
push ebp

mov esp,ebp

sub esp,0x20

为什么会出现以上代码?在主函数调用的子函数中能不能不出现这些代码?

push ebp 做的是一个保存ebp的操作,mov esp,ebp为下一个栈帧形成打下基础,在新栈帧中利用栈来保存数据,并且不会破坏原来栈帧中的数据

sub esp,0x20则是开辟了一个新的栈空间,这段空间可被函数直接利用,比如说做一些局部变量的初始化操作,也可以省略这部操作,直接通过push提高栈顶(地址下降)然后再通过mov在栈中储存数据(初始化局部变量)

主函数下的子函数结构和main函数类似,其实也可以说main函数时其他函数的子函数,没有什么较大的区别

在call的时候其实把下一条要执行汇编代码地址push进了栈中,然后在函数内部在push ebp这些操作,使得每次函数调用结束时,会出现以下代码

1
2
pop ebp
ret

ret返回的正是下一条汇编代码执行处

在函数调用时,有时通过push来传递参数,这个时候栈顶提高,但是参数传递之后对主函数没有任何作用,所以函数调用结束时主函数有时候会通过add esp来调整栈空间。

push指令在x86架构下是推入四个字节数据或地址,推入常量高位用0填充,可以通过mov指令实现对栈中数据进行字节位的调整

ret 和 retn的区别

  • ret和retn都是返回指令,用于从子程序中返回到调用处,同时从栈中弹出返回地址
  • ret和retn的区别在于是否有操作数。ret没有操作数,只是从栈中弹出返回地址;retn有一个操作数,表示要从栈中弹出的字节数
  • 例如add esp,0x4; ret;,等价于retn 0x4

repe cmpsb字符串比较汇编指令

是一个汇编语言指令,它的意思是重复比较两个字符串的字节,直到不相等或者计数器为零为止。它涉及到三个寄存器:ECX,EDI和ESI。ECX是计数器,表示要比较的字节数;EDI和ESI是两个字符串的起始地址。每次比较后,EDI和ESI会根据方向标志DF的值自动增加或减少。如果DF为0,表示正向比较,EDI和ESI都加一;如果DF为1,表示反向比较,EDI和ESI都减一。比较的结果会影响标志寄存器中的零标志ZF。如果ZF为1,表示两个字节相等;如果ZF为0,表示两个字节不等。repe cmpsb通常用来检测两个字符串是否完全相同

jmp指令偏移地址计算

在使用jmp汇编指令时,关于算偏移地址的时候机器码十六进制的计算

往下运行时,0x2 = 0x44A04D-0x44A049-0x2,也就是jmp到的地址-原来jmp前的地址-jmp这条指令的字节数,本质就是算中间有多少字节

往上运行时,0xFFF870F7 = 0x401140 - 0x44A044 - 5 最后一个五是jmp指令的字节数,0xFFF870F7转化成机器码就是F770FBFF

IDAPatch保存后动调

Keypatcher进行patch,ctrl+w保存patch结果,但是动调有影响

Apply patches to后动调即可 ,在edit-patch program里

Ollvm控制流平坦化技术

ghidra自动化去混淆工具: https://github.com/PAGalaxyLab/ghidra_scripts

IDA自动化去混淆工具d810: https://github.com/joydo/d810

Hook

Hook的多种方式

image-20240418163602878

HOOK和Callback的区别

钩子技术(Hook)和回调函数(Callback)在编程中都是用于实现程序的扩展和灵活性,但它们有着不同的作用和实现方式。

相同点

  • 事件响应: 钩子技术和回调函数都用于实现对特定事件的响应和处理。
  • 灵活性: 两者都可以增强程序的灵活性和扩展性,使得程序能够更好地处理不同的情况和需求。

差异性

  1. 作用:
  • 钩子技术主要用于拦截、监视和修改系统级别或应用程序级别的事件,比如键盘输入、鼠标操作、窗口消息等。
  • 回调函数主要用于在特定事件发生时被调用,通常用于异步编程或事件驱动编程中,比如网络请求完成后的回调函数、定时器触发后的回调函数等。
  1. 实现方式:
  • 钩子技术通常是通过操作系统提供的接口或库函数来实现的,可以在系统级别或应用程序级别进行钩子的安装和管理。
  • 回调函数是一种编程模式,通过将函数指针或函数引用传递给其他函数,以便在特定事件发生时被调用。
  1. 应用场景:
  • 钩子技术适用于需要拦截、监视和修改事件流的情况,比如实现键盘记录器、窗口管理工具等。
  • 回调函数适用于需要异步处理或事件驱动的情况,比如处理异步任务完成后的结果、处理定时器事件等。

综上所述,钩子技术和回调函数虽然都与事件响应相关,但它们的作用、实现方式和应用场景有着明显的区别。钩子技术更专注于事件的拦截和修改,而回调函数更专注于事件的响应和异步处理。

从代码实现角度来看,Hook主要分成两种,Inline Hook 和 非 Inline Hook

Inline Hook指的是:

image-20240416213218744

在call的调用的函数内里添加 jmp 想执行代码地址

非Inline Hook指的是:

image-20240416213515874

事实上就是改变了Call操作的目标地址,实际上就是IAT Hook

C++虚表的Hook

一个类的内存布局:

image-20240416214808838

通过对象的地址来获得虚表的首地址,从而获得所有虚函数的地址

C++调用fish类的虚函数代码:

1
2
fish Myfish;
Myfish.sleep();

汇编调用fish类的虚函数代码:

1
2
3
mov eax,ecx    //eca是MyFish对象的地址
mov eax,[eax] //获取MyFish对象的虚表地址
call [eax+0x04]//调用sleep()函数

Detours Hook

框架学习

1
2
3
4
5
6
7
8
9
10
11
12
   	//1.保存detour的事务
DetourRestoreAfterWith();
//2.开始处理detour的事务
DetourTransactionBegin();
//3.更新线程信息-执行事务的线程
DetourUpdateThread(GetCurrentThread());
//4.设置需要拦截的代理函数
//第一个参数二级函数指针-原函数地址
//第二个参数函数地址-代理函数的地址
DetourAttach((PVOID*)&RealMessageBox, MyMessageBox);//msg
//5.提交事务
DetourTransactionCommit();

Hook MessageBox函数使得弹窗显示永远是 “你已经被Hook了!”

mfc程序

mfc.cpp

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
#include"mfc.h"

Myapp app;//全局应用程序对象
//MyFrame* m_pMainwnd = new MyFrame;
BOOL Myapp::InitInstance()
{
MyFrame* frame = new MyFrame;
frame->ShowWindow(SW_SHOWNORMAL);
frame->UpdateWindow();
//保存程序指针,否则窗口一闪而过,
m_pMainWnd = frame;//m_pMainWnd线程类的一个成员
return 1;
}

BEGIN_MESSAGE_MAP(MyFrame, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

MyFrame::MyFrame()
{
Create(NULL, TEXT("Crackk"));
}

VOID MyFrame::OnLButtonDown(UINT, CPoint point)
{
//获取Dll1.dll文件路径
WCHAR WorkPath[MAX_PATH]; //用于存放获取路径的信息
GetModuleFileName(NULL, WorkPath, MAX_PATH);
CString DllPath = WorkPath;
int pos = DllPath.ReverseFind('\\');
DllPath = DllPath.Left(pos + 1);
DllPath += _T("Dll1.dll");
//一般情况利用辅助程序实现dll注入,这里为了便捷直接loadlibrary
HMODULE hmodule = LoadLibrary(DllPath);
if (hmodule == NULL)
{
MessageBox(NULL, (LPCTSTR)"加载dll失败", NULL);
}
else
{
MessageBox(NULL, (LPCTSTR)"弹窗", NULL);
}
}

dll程序

solve.cpp

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
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "windows.h"
#include <iostream>
#include <fstream>
#include "solve.h"

using namespace std;

BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
Start_Hook();
break;
}
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
{
Exit_Hook();
break;
}
}
return TRUE;
}

Dll1main.cpp

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
#pragma once
#include "solve.h"
#include "pch.h"
#include <detours.h>
#pragma comment(lib,"detours.lib")
int(WINAPI* RealMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT) = MessageBox;
int WINAPI MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
return RealMessageBox(hWnd, L"你已经被Hook了!", L"Hooked", uType);
}
void Start_Hook()
{
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach((PVOID*)&RealMessageBox, MyMessageBox);//msg
DetourTransactionCommit();
}
void Exit_Hook()
{
//1.开始处理detour的事务
DetourTransactionBegin();
//2.更新线程信息-执行事务的线程
DetourUpdateThread(GetCurrentThread());
//3.撤销hook
//第一个参数二级函数指针-原函数地址
//第二个参数函数地址-代理函数的地址
DetourDetach((PVOID*)&RealMessageBox, MyMessageBox);
//4.提交事务
DetourTransactionCommit();
}

image-20240416230935344

Windows的S.E.H

S.E.H全称是Struct Exception Handler即结构化异常处理机制

S.E.H是操作系统提供给线程来感知和处理异常的一种回调机制,S.E.H在线程栈上以单链表的形式存在

image-20240418155025834

在win32中由于FS寄存器总是指向当前的TIB(线程信息块),因此在FS:[0]处能找到最近的一个EXCEPTION_REGISTERATION结构

当我们通过try/catch或__try/except等操作来注册S.E.H的时候,FS:[0]会指向新的S.E.H,且新的S.E.H的Prev字段会指向之前FS:[0]指向的S.E.H,整个操作类似于单链表的表头插入操作

Windows的V.E.H

S.E.H是基于线程的异常处理,V.E.H是基于进程的异常处理

V.E.H处理异常的优先级低于调试器,高于S.E.H,即KiUserExceptionDispatcher()函数首先检查进程是否处于被调试状态,然后检查V.E.H链表,最后才是检查S.E.H链表

Windows提供了注册V.E.H的回调函数的API

1
2
3
4
5
PVOID AddVectoredExceptionHandler//注册回调函数
{
ULONG FirstHandler,//如果大于0则从头部插入,否则从尾部插入
PVECTORED_EXCEPTION_HANDLE VectoredHandler//异常处理函数地址
};

可以通过该函数进行虚假注册,通过函数返回值后去链表的头指针或者尾指针,从而遍历整个V.E.H链表

硬件断点

调试器寄存器

IA-32处理器定义了8个调试寄存器,DR0-DR7其中DR0-DR3用来指定断点的内存地址或IO地址;DR4和DR5是保留的,DR6的作用是当调试事件发生时向调试器报告事件的详细信息,以供调试器啊判断发生的是何种事件;DR7用来进一步定义中断条件

硬件断点的优势

硬件断点HOOK是结合DR0-DR3调试寄存器和Windows S.E.H 或V.E.H机制所引入的一种HOOK机制.如果HOOK采用的方式是修改代码,那么很容易被检测到,然而硬件断点HOOK并不涉及修改代码,所以他的优点主要体现在隐蔽性上

线程上下文环境以及Windows下线程与CPU之间的关系

线程执行环境CONTEXT的结构体:

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
typedef struct_CONTEXT
{
DwORD contextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD segGs;
DWORD segFs;
DWORD segEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD segCs;
DWORD EFlags;
DWORD Esp;
DWORD segSs;
} CONTEXT;

Windows是基于线程调用的多任务抢占式操作系统,如果CPU目前运行的线程1,这时优先级更高的线程2将抢占执行,那么操作系统会把线程1的执行环境取出放到线程1的CONTEXT结构中,接着把线程2的CONTEXT信息放入CPU中,继续运行线程2

S.E.H Hook

未成功代码,显示有没经处理的异常

应该是MyExceptionFilter函数没起作用

dll中

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
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "SEHHook.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
SetHardBreakPoint();
break;
case DLL_THREAD_ATTACH:
SetHardBreakPoint();
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

//SEH.cpp
#include "pch.h"
#include "SEHHook.h"
#include <climits>
DWORD HookAddr = (DWORD)GetProcAddress(GetModuleHandleA("USER32.dll"),"MessageBoxW");

LONG WINAPI MyExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (LPVOID)HookAddr)
{
//获取当前线程上下文
PCONTEXT pcontext = ExceptionInfo->ContextRecord;
MessageBoxW(NULL,TEXT("你被hook了诶"), TEXT("gogogo"),0);
pcontext->Eip += 5;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}

void SetHardBreakPoint()
{
//遍历线程 通过openthread获取到线程环境后设置硬件断点
HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hTool32 != INVALID_HANDLE_VALUE)
{
THREADENTRY32 threadEntry32;//线程环境结构体
threadEntry32.dwSize = sizeof(THREADENTRY32);
HANDLE hMainThread = NULL;
FILETIME exit_time, kernel_time, user_time;
FILETIME creation_time;
FILETIME prev_creation_time;
prev_creation_time.dwLowDateTime = 0xFFFFFFFF;
prev_creation_time.dwHighDateTime = INT_MAX;
DWORD dwThreadEntryOffset = FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(threadEntry32.th32OwnerProcessID);
if (Thread32First(hTool32, &threadEntry32))
{
do
{
if (threadEntry32.dwSize >= dwThreadEntryOffset &&threadEntry32.th32OwnerProcessID == GetCurrentProcessId())
{
HANDLE hHookThread = OpenThread(THREAD_ALL_ACCESS, false, threadEntry32.th32ThreadID);
GetThreadTimes(hHookThread, &creation_time, &exit_time, &kernel_time, &user_time);
if (CompareFileTime(&creation_time, &prev_creation_time) == -1)
{
memcpy(&prev_creation_time, &creation_time, sizeof(FILETIME));
if (hMainThread != NULL)
CloseHandle(hMainThread);
hMainThread = hHookThread;
}
else
{
CloseHandle(hHookThread);
}
}
threadEntry32.dwSize = sizeof(THREADENTRY32);
} while (Thread32Next(hTool32,&threadEntry32));
SetUnhandledExceptionFilter(MyExceptionFilter);
CONTEXT thread_context = { CONTEXT_DEBUG_REGISTERS };
thread_context.Dr0 = (DWORD)HookAddr;
thread_context.Dr7 = 0x1;//设置断点类型(访问断点)
SetThreadContext(hMainThread, &thread_context);
CloseHandle(hMainThread);
}
}
CloseHandle(hTool32);
}

V.E.H Hook

试验成功

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
//dllmain.cpp
#include "pch.h"
#include "VEHHook.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)MyExceptionFilter);
SetHardBreakPoint();
break;
case DLL_THREAD_ATTACH:
SetHardBreakPoint();
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//VEHHOOK.cpp
#include "pch.h"
#include "VEHHook.h"
#include <climits>
DWORD HookAddr = (DWORD)GetProcAddress(GetModuleHandleA("USER32.dll"),"MessageBoxW");

LONG WINAPI MyExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionAddress == (LPVOID)HookAddr)
{
//获取当前线程上下文
PCONTEXT pcontext = ExceptionInfo->ContextRecord;
MessageBoxA(NULL,"HOOK", "HOOK",1);
//需要加一段调整eip的代码使得跳出死循环
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}

BOOL SetHardBreakPoint()
{
//遍历线程 通过openthread获取到线程环境后设置硬件断点
HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hTool32 != INVALID_HANDLE_VALUE)
{
THREADENTRY32 threadEntry32;//线程环境结构体
threadEntry32.dwSize = sizeof(THREADENTRY32);
HANDLE hMainThread = NULL;
FILETIME exit_time, kernel_time, user_time;
FILETIME creation_time;
FILETIME prev_creation_time;
prev_creation_time.dwLowDateTime = 0xFFFFFFFF;
prev_creation_time.dwHighDateTime = INT_MAX;
DWORD dwThreadEntryOffset = FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(threadEntry32.th32OwnerProcessID);
if (Thread32First(hTool32, &threadEntry32))
{
do
{
if (threadEntry32.dwSize >= dwThreadEntryOffset &&threadEntry32.th32OwnerProcessID == GetCurrentProcessId())
{
HANDLE hHookThread = OpenThread(THREAD_ALL_ACCESS, false, threadEntry32.th32ThreadID);
GetThreadTimes(hHookThread, &creation_time, &exit_time, &kernel_time, &user_time);
if (CompareFileTime(&creation_time, &prev_creation_time) == -1)
{
memcpy(&prev_creation_time, &creation_time, sizeof(FILETIME));
if (hMainThread != NULL)
CloseHandle(hMainThread);
hMainThread = hHookThread;
}
else
{
CloseHandle(hHookThread);
}
}
threadEntry32.dwSize = sizeof(THREADENTRY32);
} while (Thread32Next(hTool32,&threadEntry32));
CONTEXT thread_context = { CONTEXT_DEBUG_REGISTERS };
thread_context.Dr0 = (DWORD)HookAddr;
thread_context.Dr7 = 0x1;//设置断点类型(访问断点)
SetThreadContext(hMainThread, &thread_context);
CloseHandle(hMainThread);
}
}
CloseHandle(hTool32);
return true;
}

image-20240509220917719