-
Notifications
You must be signed in to change notification settings - Fork 24
/
initial_checks.cpp
1771 lines (1638 loc) · 60.6 KB
/
initial_checks.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
/*
* ______________________.___
* \_ _____/\_ _____/| |
* | __)_ | __) | |
* | \ | \ | |
* /_______ / \___ / |___|
* \/ \/
* _________ .__ ____ __. .__ _____
* / _____/_ _ _|__| ______ _____ | |/ _| ____ |__|/ ____\____
* \_____ \\ \/ \/ / |/ ___// ___/ | < / \| \ __\/ __ \
* / \\ /| |\___ \ \___ \ | | \| | \ || | \ ___/
* /_______ / \/\_/ |__/____ >____ > |____|__ \___| /__||__| \___ >
* \/ \/ \/ \/ \/ \/
*
* EFI Swiss Knife
* An IDA plugin to improve (U)EFI reversing
*
* Copyright (C) 2016, 2017 Pedro Vilaça (fG!) - [email protected] - https://reverse.put.as
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* initial_checks.cpp
*
*/
#include "initial_checks.h"
#include <ida.hpp>
#include <idp.hpp>
#include <loader.hpp>
#include <bytes.hpp>
#include <kernwin.hpp>
#include <search.hpp>
#include <allins.hpp>
#include <segment.hpp>
#include <limits.h>
#include <auto.hpp>
#include <name.hpp>
#include <frame.hpp>
#include <struct.hpp>
#include <funcs.hpp>
#include <libgen.h>
#include <sqlite3.h>
#include "config.h"
#include "utlist.h"
#include "efi_guids.h"
#include "efi_system_tables.h"
#include "logging.h"
#include "database.h"
enum IDA_REGISTERS_X64
{
REG_RAX = 0x0,
REG_RCX = 0x1,
REG_RDX = 0x2,
REG_RBX = 0x3,
REG_RSP = 0x4,
REG_RBP = 0x5,
REG_RSI = 0x6,
REG_RDI = 0x7,
REG_R8 = 0x8,
REG_R9 = 0x9,
REG_R10 = 0xA,
REG_R11 = 0xB,
REG_R12 = 0xC,
REG_R13 = 0xD,
REG_R14 = 0xE
};
enum IDA_REGISTERS_X86
{
REG_EAX = 0x0,
REG_ECX = 0x1,
REG_EDX = 0x2,
REG_EBX = 0x3,
REG_ESP = 0x4,
REG_EBP = 0x5,
REG_ESI = 0x6,
REG_EDI = 0x7,
REG_AL = 0x10,
REG_DL = 0x12
};
enum FILE_TYPE
{
kPE = 0,
kTE
};
enum ref_type
{
kInvalid = -1,
kLoad,
kStore
};
struct address_list
{
struct address_list *next;
ea_t address;
};
enum system_services
{
kInstallProcotol = 0,
kReinstallProtocol,
kHandleProtocol,
kRegisterProtocol,
kOpenProtocol,
kLocateProtocol,
kInstallMultiProtocol,
kInvalidBoot,
/* run time services */
kGetVariable,
kSetVariable,
kInvalidRunTime
};
struct analysis_entry
{
ea_t address;
struct analysis_entry *next;
enum system_services type;
};
struct guid_stats
{
EFI_GUID guid;
struct guid_stats *next;
int count;
enum system_services type;
};
struct boot_services_analysis
{
struct analysis_entry *analysis_head;
struct guid_stats *guid_stats_head;
int installed_protocols;
};
struct boot_services_analysis g_boot_services_stats;
struct runtime_services_analysis
{
struct analysis_entry *analysis_head;
struct guid_stats *guid_stats_head;
};
struct runtime_services_analysis g_runtime_services_stats;
/* a linked list to hold boot and runtime services references for further processing */
struct service_refs
{
ea_t offset;
ea_t ref_addr;
struct service_refs *next;
};
/* a separate list for boot and runtime tables */
struct service_refs *g_boot_refs_head;
struct service_refs *g_runtime_refs_head;
static int find_system_tables(void);
static const struct services_entry lookup_boot_table(ea_t offset);
static const struct services_entry lookup_runtime_table(ea_t offset);
static int find_data_seg_guids(void);
static void make_bootservice_cmts(void);
static void make_runtimeservice_cmts(void);
static char * string_guid(EFI_GUID *guid);
static void add_guid_stats_entry(enum system_services type, EFI_GUID *guid);
static void print_boot_services_usage(void);
static void print_runtime_services_usage(void);
static void analyse_interesting_boot_services(void);
static void print_protocols_usage(void);
static void log_boot_services_usage(FILE *output_file);
static void log_runtime_services_usage(FILE *output_file);
static void log_protocols_usage(FILE *output_file);
static void sql_protocols_usage(void);
static void sql_file_entry(void);
static void sql_boot_services_usage(void);
static void sql_runtime_services_usage(void);
static int locate_boot_services_refs(void);
static int locate_runtime_services_refs(void);
static void analyse_boot_refs(void);
static void analyse_runtime_refs(void);
static void make_guid_cmt(EFI_GUID *guid, ea_t target_addr);
static void print_guid(EFI_GUID *guid);
static void analyse_interesting_runtime_services(void);
ea_t bootservices_ptr = 0;
ea_t runtimeservices_ptr = 0;
extern sqlite3 *g_db_connection;
char *g_target_guid;
void
do_initial_checks(int arg)
{
/* get target name */
char *base_name = basename(command_line_file);
if (strcmp(base_name, "body.bin") == 0)
{
char *temp = basename(dirname(command_line_file));
size_t temp_len = strlen(temp);
g_target_guid = (char*)malloc(temp_len+1);
if (g_target_guid != NULL)
{
strlcpy(g_target_guid, temp, temp_len+1);
}
}
else
{
size_t temp_len = strlen(base_name);
g_target_guid = (char*)malloc(temp_len+1);
if (g_target_guid != NULL)
{
strlcpy(g_target_guid, base_name, temp_len+1);
}
}
find_data_seg_guids();
if (find_system_tables() != 0)
{
ERROR_MSG("Failed to find required system tables.");
return;
}
locate_boot_services_refs();
locate_runtime_services_refs();
make_bootservice_cmts();
make_runtimeservice_cmts();
if (g_config.generate_stats == 1)
{
analyse_boot_refs();
analyse_runtime_refs();
analyse_interesting_boot_services();
analyse_interesting_runtime_services();
print_protocols_usage();
print_boot_services_usage();
print_runtime_services_usage();
if (g_config.output_log == 1)
{
char output_name[QMAXPATH] = {0};
qsnprintf(output_name, sizeof(output_name), "%s/log", dirname(command_line_file));
FILE *output_file = qfopen(output_name, "w+");
if (output_file == NULL)
{
ERROR_MSG("Can't open log file: %s %s.", output_name, dirname(command_line_file) );
return;
}
log_boot_services_usage(output_file);
log_runtime_services_usage(output_file);
log_protocols_usage(output_file);
qfclose(output_file);
}
}
if (g_config.output_sql)
{
open_db();
sql_file_entry();
sql_protocols_usage();
sql_boot_services_usage();
sql_runtime_services_usage();
close_db();
}
}
#pragma mark -
#pragma mark Functions to locate system tables
#pragma mark -
/*
* auxiliary function to locate the Boot Services table
* via the offsets
*/
static int
locate_bootservices_table(ea_t start_addr, ea_t end_addr, ea_t *out_addr)
{
uint16_t boot_register = 0;
int found_boot = 0;
ea_t current_addr = start_addr;
/* locate the boot services table */
do
{
decode_insn(current_addr);
/* we are looking for a mov instruction that involves the RDX register and Boot Services offset in System table */
if (cmd.itype == NN_mov && cmd.Operands[1].type == o_displ && cmd.Operands[1].phrase == 0x2)
{
if (cmd.Operands[0].type == o_reg && cmd.Operands[1].addr == 0x60)
{
boot_register = cmd.Operands[0].reg;
found_boot = 1;
}
}
/* we found the correct offset so verify if it's the right instruction and label it */
if (found_boot)
{
if (cmd.itype == NN_mov && cmd.Operands[1].type == o_reg && cmd.Operands[1].reg == boot_register && cmd.Operands[0].type == o_mem)
{
DEBUG_MSG("Found boot storage at 0x%llx Type: %x Address: 0x%llx", current_addr, cmd.Operands[0].type, cmd.Operands[0].addr);
set_name(cmd.Operands[0].addr, "BootServices_table", SN_CHECK);
*out_addr = cmd.Operands[0].addr;
break;
}
}
} while ((current_addr = find_code(current_addr, SEARCH_DOWN)) != BADADDR && current_addr <= end_addr);
/* failure */
if (found_boot == 0)
{
return 1;
}
/* success */
return 0;
}
/*
* auxiliary function to locate the RunTime Services table
* via the offsets
*/
static int
locate_runtimeservices_table(ea_t start_addr, ea_t end_addr, ea_t *out_addr)
{
uint16_t runtime_register = 0;
int found_runtime = 0;
ea_t current_addr = start_addr;
do
{
decode_insn(current_addr);
if (cmd.itype == NN_mov && cmd.Operands[1].type == o_displ && cmd.Operands[1].phrase == 0x2)
{
if (cmd.Operands[0].type == o_reg && cmd.Operands[1].addr == 0x58)
{
runtime_register = cmd.Operands[0].reg;
found_runtime = 1;
}
}
if (found_runtime)
{
if (cmd.itype == NN_mov && cmd.Operands[1].type == o_reg && cmd.Operands[1].reg == runtime_register && cmd.Operands[0].type == o_mem)
{
DEBUG_MSG("Found runtime storage at 0x%llx", current_addr);
set_name(cmd.Operands[0].addr, "RunTimeServices_table", SN_CHECK);
*out_addr = cmd.Operands[0].addr;
break;
}
}
} while ((current_addr = find_code(current_addr, SEARCH_DOWN)) != BADADDR && current_addr <= end_addr);
if (found_runtime == 0)
{
return 1;
}
/* success */
return 0;
}
/*
* entry function that scans for Boot and RunTime service tables
* labels interesting places and extracts information out of it
*
*/
static int
find_system_tables(void)
{
segment_t *seg_info = get_segm_by_name(".text");
if (seg_info == NULL)
{
ERROR_MSG("Can't find a valid code segment!");
return 1;
}
/* locate start() */
func_t *f = NULL;
int found = 0;
for (int idx = 0; idx < get_func_qty(); idx++)
{
char fname[1024] = {0};
f = getn_func(idx);
get_func_name(f->startEA, fname, sizeof(fname));
if (strcmp(fname, "start") == 0 || strcmp(fname, "_ModuleEntryPoint") == 0)
{
DEBUG_MSG("Found function %s at %llx", fname, f->startEA);
found = 1;
break;
}
}
if (found == 0)
{
ERROR_MSG("Can't find start(). Giving up...");
return 1;
}
/* we found start() so start looking for the tables */
int ret = 0;
ret = locate_bootservices_table(f->startEA, f->endEA, &bootservices_ptr);
/* try alternative method - there might be a call processing it */
if (ret != 0)
{
DEBUG_MSG("Trying to locate boot services inside functions");
ea_t current_addr = f->startEA;
while ((current_addr = find_code(current_addr, SEARCH_DOWN)) != BADADDR && current_addr <= f->endEA)
{
decode_insn(current_addr);
if (cmd.itype == NN_call) // XXX: call types? near? far?
{
func_t *call_f = NULL;
call_f = get_func(cmd.Operands[0].addr);
if (call_f == NULL)
{
continue;
}
/* try to locate the boot table inside, stop if successful */
if (locate_bootservices_table(call_f->startEA, call_f->endEA, &bootservices_ptr) == 0)
{
break;
}
}
}
}
if (bootservices_ptr == 0)
{
ERROR_MSG("Can't locate boot services table pointer.");
return 1;
}
ret = locate_runtimeservices_table(f->startEA, f->endEA, &runtimeservices_ptr);
if (ret != 0)
{
DEBUG_MSG("Trying to locate runtime services inside functions");
ea_t current_addr = f->startEA;
while ((current_addr = find_code(current_addr, SEARCH_DOWN)) != BADADDR && current_addr <= f->endEA)
{
decode_insn(current_addr);
if (cmd.itype == NN_call) // XXX: call types? near? far?
{
func_t *call_f = NULL;
call_f = get_func(cmd.Operands[0].addr);
if (call_f == NULL)
{
continue;
}
/* try to locate the runtime table inside, stop if successful */
if (locate_runtimeservices_table(call_f->startEA, call_f->endEA, &runtimeservices_ptr) == 0)
{
break;
}
}
}
}
if (runtimeservices_ptr == 0)
{
ERROR_MSG("Can't locate runtime services table pointer.");
return 1;
}
/* success */
return 0;
}
/*
* function to lookup the Boot Services table via offset
*/
static const struct services_entry
lookup_boot_table(ea_t offset)
{
size_t array_size = sizeof(boot_services_table) / sizeof(*boot_services_table);
for (int i = 0; i < array_size; i++)
{
if (boot_services_table[i].offset == offset)
{
return boot_services_table[i];
}
}
/* failure */
return boot_services_table[0];
}
/*
* function to lookup the RunTime Services table via offset
*
*/
static const struct services_entry
lookup_runtime_table(ea_t offset)
{
size_t array_size = sizeof(runtime_services_table) / sizeof(*runtime_services_table);
for (int i = 0; i < array_size; i++)
{
if (runtime_services_table[i].offset == offset)
{
return runtime_services_table[i];
}
}
/* failure */
return runtime_services_table[0];
}
static void
add_guid_stats_entry(enum system_services type, EFI_GUID *guid)
{
struct guid_stats *stats_entry = NULL;
int found_stats = 0;
LL_FOREACH(g_boot_services_stats.guid_stats_head, stats_entry)
{
if (stats_entry->type == type && memcmp((void*)&stats_entry->guid, (void*)guid, sizeof(EFI_GUID)) == 0)
{
stats_entry->count++;
found_stats = 1;
}
}
if (found_stats == 0)
{
struct guid_stats *stats_new_entry = (struct guid_stats*)malloc(sizeof(struct guid_stats));
if (stats_new_entry != NULL)
{
memcpy((void*)&stats_new_entry->guid, (void*)guid, sizeof(EFI_GUID));
stats_new_entry->count = 1;
stats_new_entry->type = type;
LL_APPEND(g_boot_services_stats.guid_stats_head, stats_new_entry);
}
}
}
/*
* function to process the calls to interesting boot services we want to gather stats on
*/
static void
analyse_interesting_boot_services(void)
{
struct analysis_entry *entry = NULL;
/* process LocateProtocol() Boot service */
/* first thing is to find out which GUIDs are used in LocateProtocol() */
LL_FOREACH(g_boot_services_stats.analysis_head, entry)
{
DEBUG_MSG("Locate protocol entry at 0x%llx", entry->address);
/* find and track the GUID */
ea_t current_addr = entry->address;
/* max number of instructions backwards we want to search */
int max_nr_insts = 32;
EFI_GUID current_guid = {0};
while ((current_addr = find_code(current_addr, SEARCH_UP)) != BADADDR && max_nr_insts > 0)
{
decode_insn(current_addr);
/* LocateProtocol() */
if (entry->type == kLocateProtocol)
{
/* GUID pointer is loaded into RCX, the first argument to LocateProtocol() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RCX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kLocateProtocol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
break;
}
}
/* HandleProtocol() */
else if (entry->type == kHandleProtocol)
{
/* GUID pointer is loaded into RDX, the second argument to HandleProtocol() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RDX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kHandleProtocol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
break;
}
}
/* RegisterProtocolNotify() */
else if (entry->type == kRegisterProtocol)
{
/* GUID pointer is loaded into RCX, the first argument to RegisterProtocolNotify() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RCX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kRegisterProtocol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
break;
}
}
/* InstallProtocolInterface () */
else if (entry->type == kInstallProcotol)
{
/* GUID pointer is loaded into RDX, the second argument to InstallProtocolInterface() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RDX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kInstallProcotol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
g_boot_services_stats.installed_protocols++;
break;
}
}
/* ReinstallProtocolInterface() */
else if (entry->type == kReinstallProtocol)
{
/* GUID pointer is loaded into RDX, the second argument to ReinstallProtocolInterface() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RDX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kReinstallProtocol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
break;
}
}
/* OpenProtocol() */
else if (entry->type == kOpenProtocol)
{
/* GUID pointer is loaded into RDX, the second argument to OpenProtocol() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RDX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kOpenProtocol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
break;
}
}
/* XXX: kInstallMultiProtocol */
/* let's assume only one protocol is installed because this version
* is recommended due to more error checking that InstallProtocol
*/
else if (entry->type == kInstallMultiProtocol)
{
/* GUID pointer is loaded into RDX, the second argument to OpenProtocol() */
if (cmd.itype == NN_lea && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == REG_RDX)
{
ea_t target_guid_addr = cmd.Operands[1].addr;
/* retrieve the GUID being used and store in the structure field */
get_many_bytes(target_guid_addr, (void*)¤t_guid, sizeof(EFI_GUID));
if (current_guid.Data1 == 0x0 || current_guid.Data1 == 0xFFFFFFFF)
{
ERROR_MSG("Invalid data retrieved, failed to identify GUID? Address: 0x%llx", current_addr);
break;
}
add_guid_stats_entry(kInstallMultiProtocol, ¤t_guid);
/* make a comment with the GUID where it's loaded */
if (g_config.comment_guid == 1)
{
make_guid_cmt(¤t_guid, current_addr);
}
g_boot_services_stats.installed_protocols++;
break;
}
}
max_nr_insts--;
}
}
}
/*
* function to go over all detected boot services references and comment it
* based on configuration settings
*/
static void
make_bootservice_cmts(void)
{
struct service_refs *ref_entry = NULL;
LL_FOREACH(g_boot_refs_head, ref_entry)
{
char address_string[4096] = {0};
struct services_entry table_entry = lookup_boot_table(ref_entry->offset);
if (g_config.comment_description == 1 && g_config.comment_prototype == 0)
{
qsnprintf(address_string, sizeof(address_string), "BootServices->%s()\n\n%s", table_entry.name, table_entry.description);
}
else if (g_config.comment_description == 0 && g_config.comment_prototype == 1)
{
qsnprintf(address_string, sizeof(address_string), "BootServices->%s()\n\n%s\n\n%s", table_entry.name, table_entry.prototype, table_entry.parameters);
}
else if (g_config.comment_description == 1 && g_config.comment_prototype == 1)
{
qsnprintf(address_string, sizeof(address_string), "BootServices->%s()\n\n%s\n\n%s\n\n%s", table_entry.name, table_entry.prototype, table_entry.description, table_entry.parameters);
}
else
{
qsnprintf(address_string, sizeof(address_string), "BootServices->%s()", table_entry.name);
}
set_cmt(ref_entry->ref_addr, address_string, 0);
}
}
/*
* auxiliary function to comment a RunTime Service call
* based on configuration settings
*/
static void
make_runtimeservice_cmts(void)
{
struct service_refs *ref_entry = NULL;
LL_FOREACH(g_runtime_refs_head, ref_entry)
{
char address_string[4096] = {0};
struct services_entry table_entry = lookup_runtime_table(ref_entry->offset);
if (g_config.comment_description == 1 && g_config.comment_prototype == 0)
{
qsnprintf(address_string, sizeof(address_string), "RunTimeServices->%s()\n\n%s", table_entry.name, table_entry.description);
}
else if (g_config.comment_description == 0 && g_config.comment_prototype == 1)
{
qsnprintf(address_string, sizeof(address_string), "RunTimeServices->%s()\n\n%s\n\n%s", table_entry.name, table_entry.prototype, table_entry.parameters);
}
else if (g_config.comment_description == 1 && g_config.comment_prototype == 1)
{
qsnprintf(address_string, sizeof(address_string), "RunTimeServices->%s()\n\n%s\n\n%s\n\n%s", table_entry.name, table_entry.prototype, table_entry.description, table_entry.parameters);
}
else
{
qsnprintf(address_string, sizeof(address_string), "RunTimeServices->%s()", table_entry.name);
}
set_cmt(ref_entry->ref_addr, address_string, 0);
}
}
/*
* locate call references to the boot services table
* this is what we use to find out where are all the calls to boot services
*/
static int
locate_boot_services_refs(void)
{
func_t *f = NULL;
if (bootservices_ptr != 0)
{
DEBUG_MSG("Looking up Boot Services references...");
xrefblk_t xb;
for ( bool ok=xb.first_to(bootservices_ptr, XREF_ALL); ok; ok=xb.next_to() )
{
ea_t ref_to_table = xb.from;
/* disassemble and process each reference */
f = get_func(ref_to_table);
ea_t current_addr = ref_to_table;
enum ref_type type;
uint16_t boot_src_reg = 0;
uint16_t boot_dst_reg = 0;
decode_insn(ref_to_table);
/* find the type of reference */
if (cmd.itype == NN_mov)
{
/* loading the boot table pointer into a register */
if (cmd.Operands[1].type == o_mem && cmd.Operands[1].addr == bootservices_ptr)
{
type = kLoad;
boot_dst_reg = cmd.Operands[0].reg;
}
/* storing the boot table pointer */
else if (cmd.Operands[0].type == o_mem && cmd.Operands[0].addr == bootservices_ptr)
{
type = kStore;
boot_src_reg = cmd.Operands[1].reg;
}
}
if (type == kInvalid)
{
continue;
}
ea_t function_end = 0;
if (f == NULL)
{
function_end = current_addr + 64;
}
else
{
function_end = f->endEA;
}
while ((current_addr = find_code(current_addr, SEARCH_DOWN)) != BADADDR && current_addr <= function_end)
{
decode_insn(current_addr);
/* verify if register was modified before the call so we give up in that case */
if (type == kStore && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == boot_src_reg)
{
break;
}
if ((cmd.itype == NN_callni || cmd.itype == NN_jmpni) && cmd.Operands[0].type == o_displ)
{
if ((type == kStore && cmd.Operands[0].phrase == boot_src_reg) ||
(type == kLoad && cmd.Operands[0].phrase == boot_dst_reg))
{
struct service_refs *new_ref_entry = (struct service_refs*)malloc(sizeof(struct service_refs));
if (new_ref_entry != NULL)
{
new_ref_entry->offset = cmd.Operands[0].addr;
new_ref_entry->ref_addr = current_addr;
LL_APPEND(g_boot_refs_head, new_ref_entry);
break;
}
}
}
}
}
}
/* success */
return 0;
}
/*
* locate call references to the runtime services table
* this is what we use to find out where are all the calls to runtime services
*/
static int
locate_runtime_services_refs(void)
{
func_t *f = NULL;
if (runtimeservices_ptr != 0)
{
DEBUG_MSG("Looking up RunTime Services references...");
xrefblk_t xb;
for ( bool ok=xb.first_to(runtimeservices_ptr, XREF_ALL); ok; ok=xb.next_to() )
{
ea_t ref_to_table = xb.from;
/* disassemble and process each reference */
f = get_func(ref_to_table);
ea_t current_addr = ref_to_table;
enum ref_type type = kInvalid;
uint16_t runtime_src_reg = 0;
uint16_t runtime_dst_reg = 0;
decode_insn(ref_to_table);
/* find the type of reference */
if (cmd.itype == NN_mov)
{
/* loading the boot table pointer into a register */
if (cmd.Operands[1].type == o_mem && cmd.Operands[1].addr == runtimeservices_ptr)
{
type = kLoad;
runtime_dst_reg = cmd.Operands[0].reg;
}
/* storing the boot table pointer */
else if (cmd.Operands[0].type == o_mem && cmd.Operands[0].addr == runtimeservices_ptr)
{
type = kStore;
runtime_src_reg = cmd.Operands[1].reg;
}
}
if (type == kInvalid)
{
continue;
}
ea_t function_end = 0;
if (f == NULL)
{
function_end = current_addr + 64;
}
else
{
function_end = f->endEA;
}
while ((current_addr = find_code(current_addr, SEARCH_DOWN)) != BADADDR && current_addr <= function_end)
{
decode_insn(current_addr);
/* verify if register was modified before the call so we give up in that case */
if (type == kStore && cmd.Operands[0].type == o_reg && cmd.Operands[0].reg == runtime_src_reg)
{
break;
}
if ((cmd.itype == NN_callni || cmd.itype == NN_jmpni) && cmd.Operands[0].type == o_displ)
{
if ((type == kStore && cmd.Operands[0].phrase == runtime_src_reg) ||
(type == kLoad && cmd.Operands[0].phrase == runtime_dst_reg))
{
struct service_refs *new_ref_entry = (struct service_refs*)malloc(sizeof(struct service_refs));
if (new_ref_entry != NULL)
{
new_ref_entry->offset = cmd.Operands[0].addr;
new_ref_entry->ref_addr = current_addr;
LL_APPEND(g_runtime_refs_head, new_ref_entry);
break;
}
}
}
}
}
}
/* success */
return 0;
}
/*
* helper function to add to analysis linked list
* the interesting services we want to extract more data later on
*/
static void
add_boot_analysis_entry(ea_t address, enum system_services type)
{
struct analysis_entry *new_entry = (struct analysis_entry*)malloc(sizeof(struct analysis_entry));
if (new_entry != NULL)
{
new_entry->address = address;
new_entry->type = type;
LL_APPEND(g_boot_services_stats.analysis_head, new_entry);
}
}
/*
* iterate over all detected locations with references to boot services table
* and generate usage stats
* and also another data set of installed/used protocol GUIDs
*/
static void
analyse_boot_refs(void)
{
struct service_refs *ref_entry = NULL;