-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhookanalysis.cpp
2950 lines (2769 loc) · 131 KB
/
hookanalysis.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
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
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "hookanalysis.h"
#define ADDRESS 0
#define TYPE 1
#define SIZE 2
#define HEAPSTATUS 3
#define FILENAME 1
#define FILEHANDLESTATUS 2
#define REGHANDLE 0
#define REGKEYVAL 1
#define REGSTATUS 2
#define MEMADDR 0
#define MEMLENGTH 1
#define MEMCONTENTTYPE 2
hookAnalysis::hookAnalysis(QStandardItemModel* tableModel, QStandardItemModel* fileViewModel,
QStandardItemModel* exceptionModel, QStandardItemModel* regeditModel,
QStandardItemModel* logWidgetModel, QStandardItemModel *netModel,
QStandardItemModel *memoryModel, QStandardItemModel *moduleModel){
this->heapViewModel = tableModel;
this->fileViewModel = fileViewModel;
this->exceptionModel = exceptionModel;
this->regeditModel = regeditModel;
this->logWidgetModel = logWidgetModel;
this->netModel = netModel;
this->memoryModel = memoryModel;
this->moduleModel = moduleModel;
}
void hookAnalysis::getModules(){
ifstream moduleFile("./hookLog/processList.txt");
QString content;
char buffer[0x10000] = {0};
moduleFile.read(buffer, 0x10000);
content = buffer;
auto mlines = content.split("\n");
for(const auto &mod : mlines){
if(mod.length() == 0)
continue;
auto ele = mod.split(" ");
moduleModel->appendRow(QList<QStandardItem*>() <<
new QStandardItem(ele[0]) <<
new QStandardItem(ele[1]) <<
new QStandardItem(ele[2]) <<
new QStandardItem(ele[3]) <<
new QStandardItem(ele[4]));
}
}
/**
* @brief hookAnalysis::appendRecord 记录分析主函数
* @param newRecord 新一条记录
* @return 处理是否产生异常
*/
bool hookAnalysis::appendRecord(QString newRecord, char *binBuf, int bufSize, bool lastRecord){
fullLog latestLog;
size_t idxptr;
bool useVar = false;
QString str;
QStringList elements;
QString val;
argType returnType;
realArg retVal;
QString line;
QList<QString> dllList;
// getCallerRIP();
// 获取进程中所有模块(exe文件、加载的dll文件)的名字、加载基地址等信息
if(!modulesGot){
getModules();
modulesGot = true;
}
// 获取该条记录的编号(此编号唯一确定一条记录,避免重复记录)
idxptr = newRecord.indexOf("ID: ");
idxptr += 4;
unsigned id = newRecord.mid(idxptr, newRecord.indexOf("\n") - idxptr).toUInt();
latestLog.id = id;
// 与前一个记录进行比较,判断是否有重复的现象出现。
if(logList.size() != 0 && !lastRecord){
fullLog lastLog = *(logList.rbegin());
if(latestLog < lastLog || latestLog == lastLog){ // 发现重复
qDebug() << latestLog.id << "<= exception";
// assert(false);
return true;
}
}else if(lastRecord && *(logList.rbegin()) == latestLog)
return true;
if(!(logList.empty() || latestLog.id == logList.rbegin()->id + 1) && latestLog.id < MAXIMUM_HOOK){
qDebug() << latestLog.id << "> exception";
assert(false);
}
// 获取该条记录的时间(函数调用的时间,精确到毫秒)
idxptr = newRecord.indexOf("DLL log output: ");
idxptr += 16;
QString logTime = newRecord.mid(idxptr, 23);
latestLog.time = logTime;
// 获取该条记录调用的函数名
idxptr = newRecord.indexOf("\n", idxptr) + 1;
size_t idxeptr = newRecord.indexOf(" ", idxptr);
QString funcName = newRecord.mid(idxptr, idxeptr - idxptr);
latestLog.funcName = funcName;
idxptr = newRecord.indexOf("Arguments:");
idxptr += 11;
// 获取所有参数
while(newRecord.mid(idxptr, 1) == "\t"){
realArg thisArg;
idxeptr = newRecord.indexOf("\n", idxptr);
QString line = newRecord.mid(idxptr + 1, idxeptr - idxptr);
idxptr = idxeptr + 1;
QStringList elements = line.split(" ");
// 获取参数类型
QString argType = elements[0];
thisArg.originalTypeName = argType;
thisArg.type = getType(argType);
// 获取参数名
QString argName = elements[1];
// 获取参数值
QString val = elements[3];
if((int)thisArg.type <= 8)
thisArg.value.imm = val.toULongLong();
else
thisArg.value.imm = val.mid(2).toULongLong(nullptr, 16);
if(argName == "dwType" && (thisArg.value.imm == REG_SZ ||
thisArg.value.imm == REG_EXPAND_SZ ||
thisArg.value.imm == REG_MULTI_SZ))
useVar = true;
// 当类型是字符串时,获取字符串的内容
QString str;
if(thisArg.type == charptr || thisArg.type == wcharptr || (useVar && argName == "lpData")){
if(elements.size() >= 6){
str = line.split("\"").at(1);
thisArg.str = new QString(str);
}
}
// 保存参数
latestLog.args.insert({argName, thisArg});
}
if(lastRecord)
goto analyse;
// 获取exe文件信息——进程名
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
if(!exeInfoGot){
exeInfo.processName = line.mid(22);
exeFileName = exeInfo.processName;
}
// 获取钩子信息——堆栈调用链
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
line = line.mid(19);
dllList = line.split("->");
latestLog.callChain = dllList;
// 获取exe加载信息——stdin句柄的值
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.STDIN = line.mid(14).toULongLong(nullptr, 16);
}
// 获取exe加载信息——stdout句柄的值
if(!exeInfoGot){
exeInfo.STDOUT = line.mid(15).toULongLong(nullptr, 16);
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
}
// 获取exe加载信息——stderr句柄的值
if(!exeInfoGot){
exeInfo.STDERR = line.mid(15).toULongLong(nullptr, 16);
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
}
// 获取exe文件信息——文件描述
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.fileDescription = line.mid(18);
}
// 获取exe文件信息——文件版本
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.fileVersion = line.mid(14);
}
// 获取exe文件信息——内部名
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.internalName = line.mid(15);
}
// 获取exe文件信息——公司名
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.companyName = line.mid(14);
}
// 获取exe文件信息——版权
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.legalCopyright = line.mid(17);
}
// 获取exe文件信息——原始名
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.originalFileName = line.mid(20);
}
// 获取exe文件信息——产品版本
if(!exeInfoGot){
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
exeInfo.productVersion = line.mid(17);
}
// 获取函数返回值,如果该返回值是一个字符串指针,则会同时保存字符串的值,所有返回值都会保存其值(整数值或指针值)
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
elements = line.split(" ");
retVal.originalTypeName = elements[2].mid(1, elements[2].length() - 2);
returnType = getType(retVal.originalTypeName);
retVal.type = returnType;
// 保存返回值的整数值
val = elements[3];
if((int)retVal.type <= 8)
retVal.value.imm = val.toLongLong();
else
retVal.value.imm = val.toULongLong(nullptr, 16);
// 保存返回值的字符串内容
if(retVal.type == charptr || retVal.type == wcharptr){
if(elements.size() >= 6)
str = line.split("\"").at(1);
}
if(!str.isEmpty())
retVal.str = new QString(str);
latestLog.retVal = retVal;
exeInfoGot = true;
// 有些函数会要求传入指针参数,将一些函数的执行结果保存到参数中的指针,如ReadFile和WriteFile就是将成功读取/写入的字符数量保存到传入的指针参数中。
// 为了全面体现函数的执行结果,这些指针在函数执行后解引用的值也应该被获取,以便及时发现任何异常情况。
if(newRecord[idxptr] != '-'){
idxeptr = newRecord.indexOf("\n", idxptr);
idxptr = idxeptr + 1;
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
// 获取需要获取执行后结果的参数值
while(line.startsWith("\t")){
line = line.mid(1);
realArg thisArg;
QStringList elements = line.split(" ");
// 获取参数类型
QString argType = elements[0];
thisArg.originalTypeName = argType;
thisArg.type = getType(argType);
// 获取参数名
QString argName = elements[1];
// 获取参数值
QString val = elements[3];
if(val.startsWith("0x"))
thisArg.value.imm = val.toULongLong(nullptr, 16);
else
thisArg.value.imm = val.toULongLong();
// 保存参数
latestLog.argsAfterCall.insert({argName, thisArg});
idxeptr = newRecord.indexOf("\n", idxptr);
line = newRecord.mid(idxptr, idxeptr - idxptr);
idxptr = idxeptr + 1;
}
}
// 如果这个函数与堆操作有关系,则进入堆分析模块进行进一步行为分析。
// 如果这个函数与文件操作有关系,则进入文件分析模块进行进一步行为分析。
// 如果这个函数与注册表操作有关系,则进入注册表分析模块进行进一步行为分析。
analyse:
if(latestLog.id == logList.size() - 1 && lastRecord)
return true;
else if(lastRecord)
latestLog.retVal.value.imm = 0xdeadbeefcafebabe;
logList.push_back(latestLog);
updateRecordBeauty(latestLog, lastRecord);
diverter(latestLog, binBuf);
return false;
}
void hookAnalysis::diverter(fullLog latestLog, char* binBuf){
QString fName = latestLog.funcName;
if(fName.contains("Heap") && analyseHeap)
analyseNewHeapInst(latestLog);
else if((fName.contains("File") || fName == "CloseHandle") && analyseFile)
analyseNewFileInst(latestLog, binBuf);
else if(fName.contains("Reg") && analyseReg)
analyseNewRegInst(latestLog);
else if(fName == "send" && analyseNet)
newSend(latestLog, binBuf);
else if(fName == "recv" && analyseNet)
newRecv(latestLog, binBuf);
else if(fName == "connect" && analyseNet)
newConnection(latestLog);
else if(fName == "bind" && analyseNet)
bindLocalSocket(latestLog);
else if(fName == "socket" && analyseNet)
addLocalUnbindedSocket(latestLog);
else if(fName == "accept" && analyseNet)
newAcception(latestLog);
}
bool hookAnalysis::updateRecordBeauty(fullLog latestLog, bool lastRecord){
int rowCount = logWidgetModel->rowCount();
logWidgetModel->appendRow(QList<QStandardItem*>() <<
new QStandardItem(to_string(latestLog.id).c_str()) <<
new QStandardItem(latestLog.funcName));
auto father = logWidgetModel->item(rowCount);
for(const auto &arg : latestLog.args){
if(arg.second.str == nullptr)
father->appendRow(QList<QStandardItem*>() <<
new QStandardItem(arg.first) <<
new QStandardItem(arg.second.originalTypeName) <<
new QStandardItem(ull2a(arg.second.value.imm)));
else
father->appendRow(QList<QStandardItem*>() <<
new QStandardItem(arg.first) <<
new QStandardItem(arg.second.originalTypeName) <<
new QStandardItem(ull2a(arg.second.value.imm)) <<
new QStandardItem(*arg.second.str));
}
if(lastRecord && latestLog.retVal.value.imm == 0xdeadbeefcafebabe)
father->appendRow(QList<QStandardItem*>() <<
new QStandardItem("返回值") <<
new QStandardItem(latestLog.retVal.originalTypeName) <<
new QStandardItem(ull2a(latestLog.retVal.value.imm)) <<
new QStandardItem("<无法获取返回值,调用本API直接导致程序崩溃>"));
else{
father->appendRow(QList<QStandardItem*>() <<
new QStandardItem("返回值") <<
new QStandardItem(latestLog.retVal.originalTypeName) <<
new QStandardItem(ull2a(latestLog.retVal.value.imm)) <<
(latestLog.retVal.str == nullptr ? nullptr : new QStandardItem(*latestLog.retVal.str)));
if(latestLog.argsAfterCall.size() != 0){
father->appendRow(QList<QStandardItem*>() << new QStandardItem("执行后部分参数解引用值"));
auto f = father->child(father->rowCount() - 1);
for(const auto &a : latestLog.argsAfterCall){
f->appendRow(QList<QStandardItem*>() <<
new QStandardItem(a.first) <<
new QStandardItem(a.second.originalTypeName) <<
new QStandardItem(ull2a(a.second.value.imm)) <<
(a.second.str ? new QStandardItem(*a.second.str) : nullptr));
}
}
}
auto callLayers = new QStandardItem("模块调用层级");
for(int i=0; i<latestLog.callChain.size(); i++)
callLayers->appendRow(new QStandardItem(latestLog.callChain[i]));
father->appendRow(QList<QStandardItem*>() << callLayers);
return true;
}
argType hookAnalysis::getType(QString input){
argType type = others;
if(input == "HANDLE" || input == "LPVOID" || input == "HKEY")
type = voidptr;
else if(input == "DWORD" || input == "UINT" || input == "REGSAM" || input == "ULONG")
type = uint32;
else if(input == "int" || input == "HFILE")
type = int32;
else if(input == "SIZE_T")
type = int64;
else if(input == "LPCSTR")
type = charptr;
else if(input == "LPCWSTR")
type = wcharptr;
else if(input == "BOOL")
type = Bool;
else if(input == "short")
type = int16;
return type;
}
// 堆结构分析入口函数,对HeapCreate、HeapDestroy、HeapAlloc、HeapFree相关HANDLE和CHUNK进行跟踪分析。
// 由于在调试过程中发现有一部分HANDLE没有被HeapCreate追踪到,因此这里需要根据HeapAlloc中传入的HANDLE类型参数判断每一个CHUNK所在的HANDLE。
// 如果这个HANDLE在之前并没有追踪到被HeapCreate所生成,那么将自动添加。由于HANDLE的大小不固定,所以HANDLE一栏不填大小,只有起始地址。
void hookAnalysis::analyseNewHeapInst(fullLog newHeapLog){
if(newHeapLog.funcName == "HeapCreate"){
// 分析HeapCreate行为
addHandle(newHeapLog);
}else if(newHeapLog.funcName == "HeapDestroy"){
// 分析HeapDestroy行为
destroyHandle(newHeapLog);
}else if(newHeapLog.funcName == "HeapAlloc"){
// 分析HeapAlloc行为
addChunk(newHeapLog);
}else if(newHeapLog.funcName == "HeapFree"){
// 分析HeapFree行为
freeChunk(newHeapLog);
}else{
exit(1);
}
}
// 文件操作分析入口函数,对OpenFile、CreateFile、ReadFile、WriteFile相关文件进行跟踪分析。
void hookAnalysis::analyseNewFileInst(fullLog newFileLog, char* binBuf){
if(newFileLog.funcName == "OpenFile"){
}else if(newFileLog.funcName == "CreateFile"){
addFileHandle(newFileLog);
}else if(newFileLog.funcName == "ReadFile"){
addReadWriteFileRecord(newFileLog, binBuf, true);
}else if(newFileLog.funcName == "WriteFile"){
addReadWriteFileRecord(newFileLog, binBuf, false);
}else if(newFileLog.funcName == "CloseHandle"){
closeFileHandle(newFileLog);
}else{
exit(1);
}
}
// 注册表操作分析入口函数
void hookAnalysis::analyseNewRegInst(fullLog newRegLog){
if(newRegLog.funcName == "RegCreateKeyEx"){
addRegKey(newRegLog);
}else if(newRegLog.funcName == "RegSetValueEx"){
setRegVal(newRegLog);
}else if(newRegLog.funcName == "RegDeleteValue"){
deleteRegValue(newRegLog);
}else if(newRegLog.funcName == "RegCloseKey"){
closeRegKey(newRegLog);
}else if(newRegLog.funcName == "RegOpenKeyEx"){
openRegKey(newRegLog);
}else if(newRegLog.funcName == "RegDeleteKeyEx"){
deleteRegKey(newRegLog);
}else
exit(1);
}
/**
* @brief hookAnalysis::addHandle 添加一个新HANDLE
* @param newHeapLog 最新一条截获日志
* @return 是否插入成功
*/
bool hookAnalysis::addHandle(fullLog newHeapLog){
uint64_t allocatedHandle = newHeapLog.retVal.value.imm; // 获取该函数的返回值,即分配到的堆起始地址
if((void*)allocatedHandle == INVALID_HANDLE_VALUE || !allocatedHandle){ // 如果返回值为0,说明分配失败,不需要进行后续操作
handleException({newHeapLog.id, AllocHandleFailed},
new exceptionInfo{.addressWithException = newHeapLog.args["dwInitialSize"].value.imm});
return false;
}
int findHandleIdx = findHandle(allocatedHandle, heapViewModel); // 查找这个HANDLE是否已经被监视
if(findHandleIdx < 0){ // 没有找到这个HANDLE,在表中新增
insertNewHeapHandle(allocatedHandle, -findHandleIdx - 1);
std::map<unsigned, bool> a;
a.insert({newHeapLog.id, true});
heapHandlesExpl->insert({allocatedHandle, a});
}else{
// 首先判断这个HANDLE目前是否正在使用,如果之前监测到明确的HeapDestroy函数将其销毁,那么再一次分配到这个HANDLE就是异常的行为。
if(heapViewModel->item(findHandleIdx, HEAPSTATUS)->text() == "已被销毁"){
handleException({newHeapLog.id, AllocSameHandle},
new exceptionInfo{.addressWithException = allocatedHandle});
}
else{
heapViewModel->item(findHandleIdx, HEAPSTATUS)->setText("正在使用"); // 修改HANDLE状态
setLineColor(heapViewModel, findHandleIdx, 150, 255, 150);
}
}
return true;
}
/**
* @brief hookAnalysis::destroyHandle 销毁一个HANDLE
* @param newHeapLog 最新一条截获日志
* @return 是否销毁成功
*/
bool hookAnalysis::destroyHandle(fullLog newHeapLog){
uint64_t destroyFinished = newHeapLog.retVal.value.imm; // 获取该函数的返回值,即是否销毁成功
uint64_t handleToDestroy = newHeapLog.args["hHeap"].value.imm;
int findHandleIdx = findHandle(handleToDestroy, heapViewModel);
if(findHandleIdx >= 0 && heapViewModel->item(findHandleIdx, HEAPSTATUS)->text() == "已被销毁"){
handleException({newHeapLog.id, DoubleDestroy},
new exceptionInfo{.addressWithException = handleToDestroy});
return false;
}
if(!destroyFinished || destroyFinished == 0xdeadbeefcafebabe){
// 如果返回值为false,说明销毁失败,查找销毁失败原因,如果要销毁的HANDLE在监视范围,则产生未知错误,如果不在则认定为无效参数
if(findHandleIdx < 0){ // 在监视列表中没有找到这个HANDLE,视为无效参数
handleException({newHeapLog.id, InvalidDestroyHandle},
new exceptionInfo{.addressWithException = handleToDestroy});
}else{
handleException({newHeapLog.id, DestroyFailed},
new exceptionInfo{.addressWithException = handleToDestroy});
}
return false;
}
// 如果返回值为true,而且要销毁的HANDLE不在监视范围内,则说明该HANDLE在创建时没有被跟踪到,不添加该HANDLE,且新增异常信息
if(findHandleIdx < 0){
handleException({newHeapLog.id, UntrackedHandleDestroyed},
new exceptionInfo{.addressWithException = handleToDestroy});
return false;
}
// 将HANDLE设置为销毁状态,同时将其内部所有CHUNK均设置为无效状态
// 会对其内部的CHUNK进行检查,如果有CHUNK正在被使用而这个HANDLE先被销毁,则目标程序再次访问这个CHUNK就会产生SIGSEGV错误。
heapViewModel->item(findHandleIdx, HEAPSTATUS)->setText("已被销毁");
setLineColor(heapViewModel, findHandleIdx, 255, 150, 150);
QStandardItem* father = heapViewModel->item(findHandleIdx);
for(int i=0; i<father->rowCount(); i++){
// 判断CHUNK的使用状态
if(father->child(i, HEAPSTATUS)->text() == "正在使用"){ // 正在使用的CHUNK所在的HANDLE被销毁
memoryLeakRisks.insert({newHeapLog.id, father->child(i)->text().toULongLong(nullptr, 16)});
handleException({newHeapLog.id, DestroyBeforeFree},
new exceptionInfo{.addressWithException = father->child(i)->text().toULongLong(nullptr, 16)});
setLineColor(father, i, 200, 100, 0);
}else
setLineColor(father, i, 200, 200, 200);
father->child(i, HEAPSTATUS)->setText("无效");
uint64_t chunkAddr = father->child(i)->text().toULongLong(nullptr, 16);
auto f = chunksExpl->find(chunkAddr);
assert(f != chunksExpl->end());
f->second.insert({newHeapLog.id, false});
}
heapHandlesExpl->find(handleToDestroy)->second.insert({newHeapLog.id, false});
return true;
}
/**
* @brief hookAnalysis::addChunk 添加一个新CHUNK,当这个CHUNK所在的HANDLE没有被追踪时,添加该HANDLE。
* @param newHeapLog 最新一条截获日志
* @param handleIdx
* @return 是否插入成功
*/
bool hookAnalysis::addChunk(fullLog newHeapLog){
uint64_t allocatedChunk = newHeapLog.retVal.value.imm; // 获取该函数的返回值,即分配到的地址
if(allocatedChunk == 0){ // 如果返回值为0,说明分配失败,不需要进行后续操作
handleException({newHeapLog.id, AllocChunkFailed},
new exceptionInfo{
.allocFail = {
.handle = newHeapLog.args["hHeap"].value.imm,
.requiredSize = newHeapLog.args["dwBytes"].value.imm
}
});
return false;
}
uint64_t handleAddr = newHeapLog.args["hHeap"].value.imm; // 获取该CHUNK所属的handle地址
int findHandleIdx = findHandle(handleAddr, heapViewModel); // 查找这个HANDLE是否已经被监视
if(findHandleIdx < 0) // 没有找到这个HANDLE,不作处理
return false;
else{
// 首先判断这个HANDLE目前是否正在使用,如果之前监测到明确的HeapDestroy函数将其销毁,那么再一次分配到这个HANDLE就是异常的行为。
// 但即使存在这种异常行为,程序依然会记录这个CHUNK,除非其返回值为nullptr。
if(heapViewModel->item(findHandleIdx, HEAPSTATUS)->text() == "已被销毁"){
handleException({newHeapLog.id, AllocToDestroyedHandle},
new exceptionInfo{.addressWithException = handleAddr});
}
// 下面将这个CHUNK保存到对应HANDLE的子项中。
// 首先需要找到这个CHUNK应该插入到哪一行。
QStandardItem* father = heapViewModel->item(findHandleIdx);
uint64_t chunkSize = newHeapLog.args["dwBytes"].value.imm;
int findChunkIdx = findChunk(allocatedChunk, father);
if(findChunkIdx < 0) // 没有找到这个CHUNK,在子表中新增
insertNewHeapChild(allocatedChunk, chunkSize, father, -findChunkIdx - 1);
else{ // 找到这个CHUNK,只需要修改其size和使用状态即可。
// 首先判断这个找到的CHUNK是否正在被使用,如果正在被使用,那么产生异常
// (这种异常经常会产生,因为有的CHUNK并不是程序本身调用malloc等堆相关函数来产生的,因此有的CHUNK被释放的操作可能不能跟踪到)
// 但这里只会对这种情况记录异常,如果size有改动还是会继续进行修改。
if(father->child(findChunkIdx, HEAPSTATUS)->text() == "正在使用" &&
heapHandlesExpl->find(handleAddr) != heapHandlesExpl->end()){
handleException({newHeapLog.id, AllocSameChunk},
new exceptionInfo{.addressWithException = allocatedChunk});
}
father->child(findChunkIdx, SIZE)->setText(ull2a(chunkSize)); // 修改size
father->child(findChunkIdx, HEAPSTATUS)->setText("正在使用"); // 修改状态
setLineColor(father, findChunkIdx, 150, 255, 150);
}
// 如果一个堆句柄没有检测到其被HeapCreate分配而是根据HeapAlloc监测到,那么这样的堆中分配和释放堆块均不计数
// 如果有一个堆块被成功分配到了已经被删除的堆句柄,且这个堆句柄明确使用HeapCreate创建,这样的堆块也不会被计数
if(heapHandlesExpl->find(handleAddr) != heapHandlesExpl->end()){
auto a = std::map<unsigned, bool>();
a.insert({newHeapLog.id, true});
chunksExpl->insert({allocatedChunk, a});
}
}
return true;
}
bool hookAnalysis::freeChunk(fullLog newHeapLog){
uint64_t success = newHeapLog.retVal.value.imm; // 本次释放是否成功
if(!success){ // 不成功,返回异常信息
handleException({newHeapLog.id, FreeChunkFailed},
new exceptionInfo{.addressWithException = newHeapLog.args["lpMem"].value.imm});
return false;
}
uint64_t victim = newHeapLog.args["lpMem"].value.imm;
uint64_t handleAddr = newHeapLog.args["hHeap"].value.imm;
// 首先根据HANDLE地址查找HANDLE,如果没有找到,说明这个CHUNK也没有被追踪,那么其大小就未知。
int findHandleIdx = findHandle(handleAddr, heapViewModel); // 查找这个HANDLE是否已经被监视
if(success == 0xdeadbeefcafebabe){ // 程序崩溃,找出原因
if(findHandleIdx < 0) // 这个堆的句柄都找不到,认定为无效CHUNK
handleException({newHeapLog.id, InvalidFree},
new exceptionInfo{.addressWithException = victim});
else{
int findChunkIdx = findChunk(victim, heapViewModel->item(findHandleIdx));
if(findChunkIdx < 0) // 能够找到堆,但是找不到这个CHUNK
handleException({newHeapLog.id, InvalidFree},
new exceptionInfo{.addressWithException = victim});
else if(heapViewModel->item(findHandleIdx)->child(findChunkIdx, HEAPSTATUS)->text() == "正在使用") // 能够找到这个CHUNK,但是释放失败而且程序还崩溃了
handleException({newHeapLog.id, FreeChunkFailed},
new exceptionInfo{.addressWithException = victim});
else{
handleException({newHeapLog.id, DoubleFree},
new exceptionInfo{.addressWithException = victim});
auto removeIter = chunksExpl->find(victim);
if(removeIter != chunksExpl->end())
removeIter->second.insert({newHeapLog.id, false});
}
}
return false;
}
if(findHandleIdx < 0){ // 没有找到这个HANDLE,不作处理
handleException({newHeapLog.id, UntrackedChunkFreed},
new exceptionInfo{.addressWithException = victim});
return true;
}
// 如果HANDLE能够被找到,再查询这个CHUNK是否被监视,如果没有则新增。
QStandardItem* father = heapViewModel->item(findHandleIdx);
int findChunkIdx = findChunk(victim, father);
if(findChunkIdx < 0){ // 没有找到这个CHUNK
insertUnknownSizeChunk(victim, father, -findChunkIdx - 1);
handleException({newHeapLog.id, UntrackedChunkFreed},
new exceptionInfo{.addressWithException = victim});
}else{ // 找到这个CHUNK
// 判断这个CHUNK是否正在被使用,如果已经释放,说明发现了Double Free
if(father->child(findChunkIdx, HEAPSTATUS)->text() == "已被释放")
handleException({newHeapLog.id, DoubleFree},
new exceptionInfo{.addressWithException = victim});
else{
father->child(findChunkIdx, HEAPSTATUS)->setText("已被释放");
setLineColor(father, findChunkIdx, 255, 150, 150);
}
auto removeIter = chunksExpl->find(victim);
if(removeIter != chunksExpl->end())
removeIter->second.insert({newHeapLog.id, false});
}
validFreeCount++;
return true;
}
/**
* @brief hookAnalysis::findHandle 根据参数的HANDLE地址从堆信息中找到对应的HANDLE位置索引
* 需要注意的是,一个HANDLE需要插入到索引为0的位置和一个HANDLE需要加入到空表中都会返回0
* 因此当CHUNK没有找到时,其返回值为(需要插入的位置 * -1) - 1,以防止歧义发生。
* @param handleAddr 需要查找的地址
* @return 索引,如果能够找到这个HANDLE,返回这个HANDLE位于哪一行,如果找不到,返回这个HANDLE应该插入在哪一行 * -1。
*/
int hookAnalysis::findHandle(uint64_t handleAddr, QStandardItemModel *Model){
int rowCount = Model->rowCount();
if(rowCount == 0)
return -1;
// int rowPtr = 0;
int floor = 0, ceil = rowCount;
if(rowCount == 0)
return -1;
if(Model->item(0)->text().toULongLong(nullptr, 16) > handleAddr)
return -1;
else if(Model->item(0)->text().toULongLong(nullptr, 16) == handleAddr)
return 0;
if(Model->item(rowCount-1)->text().toULongLong(nullptr, 16) < handleAddr)
return -rowCount-1;
else if(Model->item(rowCount-1)->text().toULongLong(nullptr, 16) == handleAddr)
return rowCount - 1;
while(floor != ceil - 1){
auto findHandle = Model->item((floor + ceil) / 2)->text().toULongLong(nullptr, 16);
if(findHandle == handleAddr)
return (floor + ceil) / 2;
else if(findHandle > handleAddr)
ceil = (floor + ceil) / 2;
else
floor = (floor + ceil) / 2;
}
return -floor-2;
}
/**
* @brief hookAnalysis::findChunk 根据参数的chunk地址从一个HANDLE中找到对应的HANDLE位置索引
* 需要注意的是,一个CHUNK需要插入到索引为0的位置和一个CHUNK需要加入到空子表中都会返回0
* 因此当CHUNK没有找到时,其返回值为(需要插入的位置 * -1) - 1,以防止歧义发生。
* @param chunkAddr 需要查找的地址
* @param father HANDLE父项对象
* @return 索引,如果能够找到这个CHUNK,返回这个CHUNK位于哪一行,如果找不到,返回这个CHUNK应该插入在哪一行 * -1。
*/
int hookAnalysis::findChunk(uint64_t chunkAddr, QStandardItem* father){
int rowCount = father->rowCount();
// int rowPtr = 0;
char* handlecmp = ull2a(chunkAddr);
int floor = 0, ceil = rowCount;
if(rowCount == 0)
return -1;
if(father->child(0)->text() > handlecmp)
return -1;
else if(father->child(0)->text() == handlecmp)
return 0;
if(father->child(rowCount-1)->text() < handlecmp)
return -rowCount-1;
else if(father->child(rowCount-1)->text() == handlecmp)
return rowCount - 1;
while(floor != ceil - 1){
auto findHandle = father->child((floor + ceil) / 2)->text();
if(findHandle == handlecmp)
return (floor + ceil) / 2;
else if(findHandle > handlecmp)
ceil = (floor + ceil) / 2;
else
floor = (floor + ceil) / 2;
}
return -floor-2;
}
int hookAnalysis::findFileInst(int id, QStandardItem* father){
int rowCount = father->rowCount();
// int rowPtr = 0;
int floor = 0, ceil = rowCount;
if(rowCount == 0)
return -1;
if(father->child(0)->text().toInt() > id)
return -1;
else if(father->child(0)->text().toInt() == id)
return 0;
if(father->child(rowCount-1)->text().toInt() < id)
return -rowCount-1;
else if(father->child(rowCount-1)->text().toInt() == id)
return rowCount - 1;
while(floor != ceil - 1){
auto findHandle = father->child((floor + ceil) / 2)->text().toInt();
if(findHandle == id)
return (floor + ceil) / 2;
else if(findHandle > id)
ceil = (floor + ceil) / 2;
else
floor = (floor + ceil) / 2;
}
return -floor-2;
}
/**
* @brief hookAnalysis::findChunk 根据参数的handle地址从列表中找到对应注册表句柄位置索引
* @param chunkAddr 需要查找的地址
* @param father HANDLE父项对象
* @return 索引,如果能够找到这个handle,返回这个CHUNK位于哪一行,如果找不到,返回这个handle应该插入在哪一行 * -1。
*/
int hookAnalysis::findRegKey(uint64_t handleAddr){
return findHandle(handleAddr, regeditModel);
}
void hookAnalysis::insertNewHeapHandle(uint64_t handleAddr, int insPos){
heapViewModel->insertRow(insPos, QList<QStandardItem*>() <<
new QStandardItem(ull2a(handleAddr)) <<
new QStandardItem("HANDLE") << new QStandardItem() <<
new QStandardItem("正在使用"));
setLineColor(heapViewModel, insPos, 150, 255, 150);
}
void hookAnalysis::insertNewHeapChild(uint64_t chunkAddr, uint64_t size, QStandardItem* father, int insPos){
father->insertRow(insPos, QList<QStandardItem*>() << new QStandardItem(ull2a(chunkAddr)) <<
new QStandardItem("CHUNK") <<
new QStandardItem(ull2a(size)) <<
new QStandardItem("正在使用"));
setLineColor(father, insPos, 150, 255, 150);
}
void hookAnalysis::insertUnknownSizeChunk(uint64_t chunkAddr, QStandardItem* father, int insPos){
father->insertRow(insPos, QList<QStandardItem*>() << new QStandardItem(ull2a(chunkAddr)) <<
new QStandardItem("CHUNK") <<
new QStandardItem("???") <<
new QStandardItem("已被释放"));
setLineColor(father, insPos, 255, 150, 150);
}
/**
* @brief hookAnalysis::addFileHandle 添加一个新的文件句柄跟踪
* @param newFileLog 最新操作日志
* @return 是否添加成功
*/
bool hookAnalysis::addFileHandle(fullLog newFileLog){
// 首先判断文件句柄是否成功获取
uint64_t returnHandle = newFileLog.retVal.value.imm;
QString fileName = *newFileLog.args["lpFileName"].str;
// 如果句柄无效
if(returnHandle == (uint64_t)INVALID_HANDLE_VALUE){
handleException({newFileLog.id, CreateFileFailed},
new exceptionInfo{.fileName = newFileLog.args["lpFileName"].str});
return false;
}
// 如果句柄有效,首先查找表中是否已经存在这个句柄
int findHandleIdx = findHandle(returnHandle, fileViewModel);
if(findHandleIdx >= 0 && fileViewModel->item(findHandleIdx, FILEHANDLESTATUS)->text() == "正在使用"){ // 如果查找到了这个HANDLE,输出异常信息
handleException({newFileLog.id, RepeatedFileHandle},
new exceptionInfo{.addressWithException = returnHandle});
return false;
}else if(findHandleIdx >= 0 && fileViewModel->item(findHandleIdx, FILEHANDLESTATUS)->text() == "已被关闭"){
// 句柄存在,但已经被关闭,则重新分配这个句柄
extractDir(newFileLog);
fileViewModel->item(findHandleIdx, FILENAME)->setText(fileName);
fileViewModel->item(findHandleIdx, FILEHANDLESTATUS)->setText("正在使用");
setLineColor(fileViewModel, findHandleIdx, 150, 255, 150);
fileViewModel->item(findHandleIdx)->appendRow(QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem("OPEN") <<
new QStandardItem(fileName) << new QStandardItem() <<
new QStandardItem("SUCCESS"));
setLineColor(fileViewModel->item(findHandleIdx), fileViewModel->item(findHandleIdx)->rowCount() - 1, 200, 255, 200);
insertNewFileHandle(newFileLog, findHandleIdx);
return true;
}
// 如果没有这个句柄,则插入这个句柄,展示相关信息
extractDir(newFileLog);
insertNewFileHandle(newFileLog, findHandleIdx);
return true;
}
/**
* @brief hookAnalysis::addReadFileRecord 添加一个对于指定文件的读取操作的记录
* @param newFileLog 最新操作日志
* @return 是否添加成功
*/
bool hookAnalysis::addReadWriteFileRecord(fullLog newFileLog, char* binBuf, bool isRead){
// 文件是否读取成功
bool success = newFileLog.retVal.value.imm;
// 是否与exe文件相关联
bool relatedToExe = false;
// 操作的文件句柄
uint64_t handle = newFileLog.args["hFile"].value.imm;
uint64_t address = newFileLog.args["lpBuffer"].value.imm;
// 如果这个文件句柄是标准输入输出句柄,则不进行分析
if(handle == exeInfo.STDIN || handle == exeInfo.STDOUT || handle == exeInfo.STDERR)
return true;
// 检查这个文件句柄是否正在被监控,如果没有则放弃本次添加
int findHandleIdx = findHandle(handle, fileViewModel);
if(findHandleIdx < 0){ // 如果没有查找到这个HANDLE,输出异常信息
handleException({newFileLog.id, UntrackedFileHandle},
new exceptionInfo{.addressWithException = handle});
return false;
}
auto father = fileViewModel->item(findHandleIdx);
QString fName = fileViewModel->item(findHandleIdx, FILENAME)->text();
QString fileSuffix = fName.split(".").last();
if(fileSuffix == "exe" || fileSuffix == "dll" || fileSuffix == "ocx" || fileSuffix == "bat" ||
fileSuffix == "vbs"){
relatedToExe = true;
if(isRead){
handleException({newFileLog.id, TryReadExecutableFile},
new exceptionInfo{.fileName = new QString(fName)});
}else{
handleException({newFileLog.id, TryWriteExecutableFile},
new exceptionInfo{.fileName = new QString(fName)});
}
}
if(isRead){
bufContent type = relatedToExe ? ExeFileContent_FROMFILE : ToBeCatagorized_FROMFILE;
// 要读取的字节数
uint64_t requiredBytes = newFileLog.args["nNumberOfBytesToRead"].value.imm;
// 实际读取的字节数
uint64_t actualBytes = newFileLog.argsAfterCall["lpNumberOfBytesRead"].value.imm;
father->insertRow(father->rowCount(), QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem("READ") <<
new QStandardItem(ull2a(requiredBytes)) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(success ? "SUCCESS" : "FAILED"));
setLineColor(father, father->rowCount() - 1, 255, 255, 200);
type = addMemory(newFileLog.id, address, binBuf, actualBytes, type);
int memInsIdx = findHandle(address, memoryModel);
if(memInsIdx < 0){
memoryModel->insertRow(-memInsIdx-1, QList<QStandardItem*>() <<
new QStandardItem(ull2a(address)) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(bufType[(int)type]));
memoryModel->item(-memInsIdx-1)->appendRow(QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(memInstType[((int)type & 12) >> 2]) <<
new QStandardItem(fName));
setLineColor(memoryModel->item(-memInsIdx-1), memoryModel->item(-memInsIdx-1)->rowCount() - 1, 255, 255, 200);
}else{
if(actualBytes > 0)
memoryModel->item(memInsIdx, 2)->setText(bufType[(int)type]);
memoryModel->item(memInsIdx)->appendRow(QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(memInstType[((int)type & 12) >> 2]) <<
new QStandardItem(fName));
setLineColor(memoryModel->item(memInsIdx), memoryModel->item(memInsIdx)->rowCount() - 1, 255, 255, 200);
}
}else{
bufContent type = relatedToExe ? ExeFileContent_TOFILE : ToBeCatagorized_TOFILE;
// 要写入的字节数
uint64_t requiredBytes = newFileLog.args["nNumberOfBytesToWrite"].value.imm;
// 实际写入的字节数
uint64_t actualBytes = newFileLog.argsAfterCall["lpNumberOfBytesWritten"].value.imm;
father->insertRow(father->rowCount(), QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem("WRITE") <<
new QStandardItem(ull2a(requiredBytes)) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(success ? "SUCCESS" : "FAILED"));
setLineColor(father, father->rowCount() - 1, 200, 200, 255);
type = addMemory(newFileLog.id, address, binBuf, actualBytes, type);
int memInsIdx = findHandle(address, memoryModel);
if(memInsIdx < 0){
memoryModel->insertRow(-memInsIdx-1, QList<QStandardItem*>() <<
new QStandardItem(ull2a(address)) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(bufType[(int)type]));
memoryModel->item(-memInsIdx-1)->appendRow(QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(memInstType[((int)type & 12) >> 2]) <<
new QStandardItem(fName));
setLineColor(memoryModel->item(-memInsIdx-1), memoryModel->item(-memInsIdx-1)->rowCount() - 1, 200, 200, 255);
}else{
if(memoryModel->item(memInsIdx, 2)->text().contains("接收") && relatedToExe)
handleException({newFileLog.id, SaveExeContentFromNet},
new exceptionInfo{.fileName = new QString(fName)});
memoryModel->item(memInsIdx)->appendRow(QList<QStandardItem*>() <<
new QStandardItem(to_string(newFileLog.id).c_str()) <<
new QStandardItem(ull2a(actualBytes)) <<
new QStandardItem(memInstType[((int)type & 12) >> 2]) <<
new QStandardItem(fName));
setLineColor(memoryModel->item(memInsIdx), memoryModel->item(memInsIdx)->rowCount() - 1, 200, 200, 255);
}
}
return true;
}