-
Notifications
You must be signed in to change notification settings - Fork 169
/
linux_procinfo.cpp
1900 lines (1689 loc) · 58.8 KB
/
linux_procinfo.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
/*
Copyright (C) <2012> <Syracuse System Security (Sycure) Lab>
DECAF is based on QEMU, a whole-system emulator. You can redistribute
and modify it under the terms of the GNU GPL, version 3 or later,
but it is made available WITHOUT ANY WARRANTY. See the top-level
README file for more details.
For more information about DECAF and other softwares, see our
web site at:
http://sycurelab.ecs.syr.edu/
If you have any questions about DECAF,please post it on
http://code.google.com/p/decaf-platform/
*/
/*
* linux_procinfo.cpp
*
* Created on: September, 2013
* Author: Kevin Wang, Lok Yan
*/
#include <inttypes.h>
#include <string>
#include <list>
#include <vector>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <tr1/unordered_map>
#include <tr1/unordered_set>
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/lexical_cast.hpp>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <queue>
#include <sys/time.h>
#include <math.h>
#include <glib.h>
#include <mcheck.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include "cpu.h"
#include "config.h"
#include "hw/hw.h" // AWH
#include "DECAF_main.h"
#include "DECAF_target.h"
#ifdef __cplusplus
};
#endif /* __cplusplus */
#include "linux_procinfo.h"
#include "hookapi.h"
#include "function_map.h"
#include "shared/vmi.h"
#include "DECAF_main.h"
#include "shared/utils/SimpleCallback.h"
#ifdef TARGET_I386
#define T_FMT ""
#define PI_R_EAX "eax"
#define PI_R_ESP "esp"
/*
#define isPrintableASCII(_x) ( ((_x & 0x80808080) == 0) \
&& ((_x & 0xE0E0E0E0) != 0) )
*/
#elif defined(TARGET_ARM)
#define T_FMT ""
#define PI_R_EAX "r0"
#define PI_R_ESP "sp"
/*
#define isPrintableASCII(_x) ( ((_x & 0x80808080) == 0) \
&& ((_x & 0xE0E0E0E0) != 0) )
*/
#else
#define T_FMT "ll"
#define PI_R_EAX "rax"
#define PI_R_ESP "rsp"
/*
#define isPrintableASCII(_x) ( ((_x & 0x8080808080808080) == 0) \
&& ((_x & 0xE0E0E0E0E0E0E0E0) != 0) )
*/
#endif
static inline int isPrintableASCII(target_ulong x)
{
int i = 0;
char c = 0;
int ret = 0;
do
{
c = (x >> i) & 0xFF; //get the next character
if ( ((c & 0x80) == 0) && ((c & 0xE0) != 0) )
{
ret = 1;
}
else if (c != 0)
{
return (0); //we found a non printable non NULL character so end it
}
i+=8; //shift it over 1 byte
} while ( (c != 0) && (i < 64) ); //while its not the NULL character
return (ret);
}
typedef target_ptr gva_t;
typedef target_ulong gpa_t;
typedef target_int target_pid_t;
//Here are some definitions straight from page_types.h
#define INV_ADDR ((target_ulong) -1)
#define INV_OFFSET ((target_ulong) -1)
#define INV_UINT ((target_uint) -1)
#if defined(TARGET_I386) || defined(TARGET_ARM)// || defined(TARGET_MIPS)
//this is the default value - but keep in mind that a custom built
// kernel can change this
#define TARGET_PAGE_OFFSET 0xC0000000
//defined this extra constant here so that the code
// for isKernelAddress can be standardized
#define TARGET_KERNEL_IMAGE_START TARGET_PAGE_OFFSET
#define TARGET_MIN_STACK_START 0xA0000000 //trial and error?
#define TARGET_KERNEL_IMAGE_SIZE (0)
#elif defined(TARGET_MIPS)
#define TARGET_PAGE_OFFSET 0x80000000UL
#define TARGET_KERNEL_IMAGE_START TARGET_PAGE_OFFSET
#define TARGET_MIN_STACK_START 0xA0000000UL //trial and error?
#define TARGET_KERNEL_IMAGE_SIZE (0)
#else
//See: http://lxr.linux.no/#linux+v3.11/Documentation/x86/x86_64/mm.txt
// for the memory regions
//These ranges seem to work for 2.6.32 as well
//these definitions are in page_64_types.h
//2.6.28 uses 81 to c0ffff -- 46bits of memory
//2.6.29 uses 88 - c0ffff -- 57TB it says
//2.6.31 uses 88 - c7ffff -- 64tb of memory - man they sure like to change these things!!!!
#define TARGET_PAGE_OFFSET 0xFFFF880000000000
//this is consistent from 2.6.28
#define TARGET_KERNEL_IMAGE_START 0xFFFFFFFF80000000
#define TARGET_MIN_STACK_START 0x0000000100000000 //trial and error?
#define TARGET_KERNEL_IMAGE_SIZE (512 * 1024 * 1024)
#endif //target_i386
//straight from the kernel in processor.h
#define TARGET_TASK_SIZE TARGET_PAGE_OFFSET
#define TARGET_KERNEL_START TARGET_TASK_SIZE
#if defined(TARGET_I386)
//got this value from testing - not necessarily true though
//might be some devices mapped into physical memory
// that will screw things up a bit
#define TARGET_KERNEL_END (0xF8000000 - 1)
#elif defined(TARGET_ARM)
// NOTICE in ARM when RAM size is less than 896, then high_memory is equal to actual RAM size
// check this link: http://www.arm.linux.org.uk/developer/memory.txt
#define TARGET_KERNEL_END (TARGET_PAGE_OFFSET + ((ram_size < 896 * 1024 * 1024) ? (ram_size - 1) : (0xF8000000 - 1)))
#else
//same here - in fact the global stuff (from the kernel image) are defined in higher addresses
#define TARGET_KERNEL_END 0xFFFFC80000000000
#endif //target_i386
//some definitions to help limit how much to search
// these will likely have to be adjusted for 64 bit, 20, 4k and 100 works for 32
#define MAX_THREAD_INFO_SEARCH_SIZE 20
#define MAX_TASK_STRUCT_SEARCH_SIZE 4000
#define MAX_MM_STRUCT_SEARCH_SIZE 500
#define MAX_VM_AREA_STRUCT_SEARCH_SIZE 500
#define MAX_CRED_STRUCT_SEARCH_SIZE 200
#define MAX_DENTRY_STRUCT_SEARCH_SIZE 200
//the list head contains two pointers thus
#define SIZEOF_LIST_HEAD (sizeof(target_ptr) + sizeof(target_ptr))
#define SIZEOF_COMM ((target_ulong)16)
#define TARGET_PGD_MASK TARGET_PAGE_MASK
#define TARGET_PGD_TO_CR3(_pgd) (_pgd - TARGET_KERNEL_START) //this is a guess
// but at any rate, PGD virtual address, which must be converted into
// a physical address - see load_cr3 function in arch/x86/include/asm/processor.h
//here is a simple function that I wrote for
// use in this kernel module, but you get the idea
// the problem is that not all addresses above
// 0xC0000000 are valid, some are not
// depending on whether the virtual address range is used
// we can figure this out by searching through the page tables
static inline
int isKernelAddress(gva_t addr)
{
return (
//the normal kernel memory area
( (addr >= TARGET_KERNEL_START) && (addr < TARGET_KERNEL_END) )
//OR the kernel image area - in case the kernel image was mapped to some
// other virtual address region - as from x86_64
|| ( (addr >= TARGET_KERNEL_IMAGE_START) && (addr < (TARGET_KERNEL_IMAGE_START + TARGET_KERNEL_IMAGE_SIZE)) )
#ifdef TARGET_X86_64
//needed to incorporate the vmalloc/ioremap space according to the
// mm.txt in documentation/x86/x86_64 of the source
//this change has been incorporated since 2.6.31
//2.6.30 used c2 - e1ffff as the range.
|| ( (addr >= 0xFFFFC90000000000) && (addr < 0xFFFFe90000000000) )
#endif
);
}
static inline int isKernelAddressOrNULL(gva_t addr)
{
return ( (addr == (gva_t)0) || (isKernelAddress(addr)) );
}
inline int isStructKernelAddress(gva_t addr, target_ulong structSize)
{
return ( isKernelAddress(addr) && isKernelAddress(addr + structSize) );
}
target_ulong getESP(CPUState *env)
{
return DECAF_getESP(env);
}
gpa_t getPGD(CPUState *env)
{
return (DECAF_getPGD(env) & TARGET_PGD_MASK);
}
//We will have to replace this function with another one - such as
// read_mem in DECAF
static inline target_ulong get_target_ulong_at(CPUState *env, gva_t addr)
{
target_ulong val;
if (DECAF_read_mem(env, addr, sizeof(target_ulong), &val) < 0)
return (INV_ADDR);
return val;
}
static inline target_uint get_uint32_at(CPUState *env, gva_t addr)
{
target_uint val;
if (DECAF_read_mem(env, addr, sizeof(uint32_t), &val) < 0)
return (INV_UINT);
return val;
}
//Dangerous memcpy
static inline int get_mem_at(CPUState *env, gva_t addr, void* buf, size_t count)
{
return DECAF_read_mem(env, addr, count, buf) < 0 ? 0 : count;
}
//The idea is to go through the data structures and find an
// item that points back to the threadinfo
//ASSUMES PTR byte aligned
// gva_t findTaskStructFromThreadInfo(CPUState * env, gva_t threadinfo, ProcInfo* pPI, int bDoubleCheck) __attribute__((optimize("O0")));
gva_t findTaskStructFromThreadInfo(CPUState * env, gva_t threadinfo, ProcInfo* pPI, int bDoubleCheck)
{
int bFound = 0;
target_ulong i = 0;
target_ulong j = 0;
gva_t temp = 0;
gva_t temp2 = 0;
gva_t candidate = 0;
gva_t ret = INV_ADDR;
if (pPI == NULL)
{
return (INV_ADDR);
}
//iterate through the thread info structure
for (i = 0; i < MAX_THREAD_INFO_SEARCH_SIZE; i+= sizeof(target_ptr))
{
temp = (threadinfo + i);
candidate = 0;
// candidate = (get_target_ulong_at(env, temp));
DECAF_read_ptr(env, temp, &candidate);
//if it looks like a kernel address
if (isKernelAddress(candidate))
{
//iterate through the potential task struct
for (j = 0; j < MAX_TASK_STRUCT_SEARCH_SIZE; j+= sizeof(target_ptr))
{
temp2 = (candidate + j);
//if there is an entry that has the same
// value as threadinfo then we are set
target_ulong val = 0;
DECAF_read_ptr(env, temp2, &val);
if (val == threadinfo)
{
if (bFound)
{
//printk(KERN_INFO "in findTaskStructFromThreadInfo: Double Check failed\n");
return (INV_ADDR);
}
pPI->ti_task = i;
pPI->ts_stack = j;
ret = candidate;
if (!bDoubleCheck)
{
return (ret);
}
else
{
//printk(KERN_INFO "TASK STRUCT @ [0x%"T_FMT"x] FOUND @ offset %"T_FMT"d\n", candidate, j);
bFound = 1;
}
}
}
}
}
return (ret);
}
gpa_t findPGDFromMMStruct(CPUState * env, gva_t mm, ProcInfo* pPI, int bDoubleCheck)
{
target_ulong i = 0;
gpa_t temp = 0;
gpa_t pgd = getPGD(env);
if (pPI == NULL)
{
return (INV_ADDR);
}
if ( !isStructKernelAddress(mm, MAX_MM_STRUCT_SEARCH_SIZE) )
{
return (INV_ADDR);
}
for (i = 0; i < MAX_MM_STRUCT_SEARCH_SIZE; i += sizeof(target_ulong))
{
temp = get_target_ulong_at(env, mm + i);
if (temp == INV_ADDR)
return (INV_ADDR);
if (pgd == TARGET_PGD_TO_CR3((temp & TARGET_PGD_MASK)))
{
if (pPI->mm_pgd == INV_OFFSET)
pPI->mm_pgd = i;
return (temp);
}
}
return (INV_ADDR);
}
gva_t findMMStructFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI, int bDoubleCheck)
{
if (pPI == NULL)
{
return (INV_ADDR);
}
if ( !isStructKernelAddress(ts, MAX_TASK_STRUCT_SEARCH_SIZE) )
{
return (INV_ADDR);
}
for (target_ulong i = 0, temp = INV_ADDR, last_mm; i < MAX_TASK_STRUCT_SEARCH_SIZE; i += sizeof(target_ulong))
{
last_mm = temp; // record last mm value we read, so that we don't need read it twice
temp = get_target_ulong_at(env, ts + i);
if (isKernelAddress(temp))
{
if (findPGDFromMMStruct(env, temp, pPI, bDoubleCheck) != INV_ADDR)
{
// ATTENTION!! One interesting thing here is, when the system just starts, highly likely
// we will grab the active_mm instead of mm, as the mm is null for kernel thread
if (pPI->ts_mm == INV_OFFSET) {
// do a test, if this is mm, then active_mm should be equal to mm
if (get_target_ulong_at(env, ts + i + sizeof(target_ulong)) == temp) {
pPI->ts_mm = i;
//monitor_printf(default_mon, "mm and active_mm is the same! \n");
}
else if (last_mm == 0) { // current mm may be active_mm
pPI->ts_mm = i - sizeof(target_ulong);
//monitor_printf(default_mon, "mm is null, active_mm detected! \n");
}
else
break; // something is wrong
}
return (temp);
}
}
}
return (INV_ADDR);
}
//the characteristic of task struct list is that next is followed by previous
//both of which are pointers
// furthermore, next->previous should be equal to self
// same with previous->next
//lh is the list_head (or supposedly list head)
int isListHead(CPUState * env, gva_t lh)
{
gva_t pPrev = lh + sizeof(target_ulong);
gva_t next = 0;
gva_t prev = 0;
if ( !isKernelAddress(lh) || !isKernelAddress(pPrev) )
{
return (0);
}
//if both lh and lh+target_ulong (previous pointer) are pointers
// then we can dereference them
next = get_target_ulong_at(env, lh);
prev = get_target_ulong_at(env, pPrev);
if ( !isKernelAddress(next) || !isKernelAddress(prev) )
{
return (0);
}
// if the actual dereferences are also pointers (because they should be)
// then we can check if the next pointer's previous pointer are the same
if ( (get_target_ulong_at(env, prev) == lh)
&& (get_target_ulong_at(env, next + sizeof(target_ulong)) == lh)
)
{
return (1);
}
return (0);
}
//TODO: DoubleCheck
//In this case, because there are so many different list_head
// definitions, we are going to use the first
// list head when searching backwards from the mm struct
//The signature that we use to find the task struct is the following (checked against
// version 3.9.5 and 2.6.32)
// depending on whether SMP is configured or not (in 3.9.5) we should see the following
// list_head (tasks) //8 bytes
// int PRIO (if SMP in 3.9.5) //4 bytes
// list_head (plist_node->plist_head->list_head in 2.6.32, and if SMP in 3.9.5) // 8 bytes
// list_head (same // 8 bytes
// spinlock* (optional in 2.6.32 if CONFIG_DEBUG_PI_LIST is set)
//So the idea is that we will see if we have a listhead followed by an int followed by
// 2 list heads followed by mm struct (basically we search backwards from mmstruct
// if this pattern is found, then we should have the task struct offset
gva_t findTaskStructListFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI, int bDoubleCheck)
{
target_ulong i = 0;
gva_t temp = 0;
if (pPI == NULL)
{
return (INV_ADDR);
}
//this works for -1 as well (the default value) since we are using target_ulong
if (pPI->ts_mm >= MAX_TASK_STRUCT_SEARCH_SIZE)
{
return (INV_ADDR);
}
//must check the whole range (includes overflow)
if ( !isStructKernelAddress(ts, MAX_TASK_STRUCT_SEARCH_SIZE) )
{
return (INV_ADDR);
}
//We use the first such doubly linked list that comes before the
// mmstruct pointer, 28 is the size of the template
// 3 list heads plus an int
//The reason for this type of search is because in 2.6 we have
// list_head tasks, followed by plist_head (which is an int followed
// by two list heads - might even have an additional pointer even - TODO - handle that case
//in kernel version 3, the plist_node is wrapped by a CONFIG_SMP #def
//TODO: Double check that target_ulong is the right way to go
// the idea is that plist_node actually uses an int - however in 64 bit
// systems, the fact that list_head defines a pointer - it would imply that
// the int prio should take up 8 bytes anyways (so that things are aligned properly)
for (i = (SIZEOF_LIST_HEAD * 3 + sizeof(target_ulong)); i < pPI->ts_mm; i+=sizeof(target_ulong))
{
temp = ts + pPI->ts_mm - i;
//if its a list head - then we can be sure that this should work
if (isListHead(env, temp))
{
//printk(KERN_INFO "[i = %"T_FMT"d] %d, %d, %d, --- \n", i, isListHead(env, temp)
// , isListHead(env, temp + SIZEOF_LIST_HEAD + sizeof(target_ulong))
// , isListHead(env, temp + SIZEOF_LIST_HEAD + SIZEOF_LIST_HEAD + sizeof(target_ulong))
// );
if ( isListHead(env, temp + SIZEOF_LIST_HEAD + sizeof(target_ulong))
&& isListHead(env, temp + SIZEOF_LIST_HEAD +SIZEOF_LIST_HEAD + sizeof(target_ulong))
)
{
//printk(KERN_INFO "FOUND task_struct_list offset [%d]\n", (uint32_t)temp - ts);
pPI->ts_tasks = temp - ts;
return (get_target_ulong_at(env, temp));
}
}
}
//if we are here - then that means we did not find the pattern - which could be because
// we don't have smp configured on a 3 kernel
//TODO: enable and test this part - needs a second level check later just in case
// this was incorrect
for (i = sizeof(target_ulong); i < pPI->ts_mm; i += sizeof(target_ulong))
{
temp = ts + pPI->ts_mm - i;
if (isListHead(env, temp))
{
pPI->ts_tasks = temp - ts;
return (get_target_ulong_at(env, temp));
}
}
return (INV_ADDR);
}
//basically uses the threadinfo test to see if the current is a task struct
//We also use the task_list as an additional precaution since
// the offset of the threadinfo (i.e., stack) is 4 and the offset of
// the task_struct in threadinfo is 0 which just happens to correspond
// to previous and next if this ts was the address of a list_head
// instead
//TODO: Find another invariance instead of the tasks list?
int isTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI)
{
gva_t temp = 0;
gva_t temp2 = 0;
if (pPI == NULL)
{
return (0);
}
if (!isStructKernelAddress(ts, MAX_TASK_STRUCT_SEARCH_SIZE))
{
return (0);
}
if ( (pPI->ts_stack == INV_OFFSET) || (pPI->ti_task == INV_OFFSET) )
{
return (0);
}
temp = ts + pPI->ts_stack;
//dereference temp to get to the TI and then add the offset to get back
// the pointer to the task struct
temp2 = get_target_ulong_at(env, temp) + pPI->ti_task;
if ( !isKernelAddress(temp2) )
{
return (0);
}
//now see if the tasks is correct
if ( !isListHead(env, ts + pPI->ts_tasks) )
{
return (0);
}
return (1);
}
//the signature for real_parent is that this is the
// first item where two task_struct pointers are together
// real_parent is the first one (this should most likely
// be the same as parent, although not necessarily true)
//NOTE: We can also use the follow on items which are
// two list_heads for "children" and "sibling" as well
// as the final one which is a task_struct for "group_leader"
gva_t findRealParentGroupLeaderFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI)
{
target_ulong i = 0;
if (pPI == NULL)
{
return (INV_ADDR);
}
for (i = 0; i < MAX_TASK_STRUCT_SEARCH_SIZE; i += sizeof(target_ulong))
{
if ( isTaskStruct(env, get_target_ulong_at(env, ts+i), pPI) //real_parent
&& isTaskStruct(env, get_target_ulong_at(env, ts+i+sizeof(target_ulong)), pPI) //parent
&& isListHead(env, ts+i+sizeof(target_ulong)+sizeof(target_ulong)) //children
&& isListHead(env, ts+i+sizeof(target_ulong)+sizeof(target_ulong)+SIZEOF_LIST_HEAD) //sibling
&& isTaskStruct(env, get_target_ulong_at(env, ts+i+sizeof(target_ulong)+sizeof(target_ulong)+SIZEOF_LIST_HEAD+SIZEOF_LIST_HEAD), pPI) //group_leader
)
{
if (pPI->ts_real_parent == INV_OFFSET)
{
pPI->ts_real_parent = i;
}
if (pPI->ts_group_leader == INV_OFFSET)
{
pPI->ts_group_leader = i+sizeof(target_ulong)+sizeof(target_ulong)+SIZEOF_LIST_HEAD+SIZEOF_LIST_HEAD;
}
return (ts+i);
}
}
return (INV_ADDR);
}
//The characteristics of the init_task that we use are
//The mm struct pointer is NULL - since it shouldn't be scheduled?
//The parent and real_parent is itself
int isInitTask(CPUState * env, gva_t ts, ProcInfo* pPI, int bDoubleCheck)
{
int bMMCheck = 0;
int bRPCheck = 0;
if ( (pPI == NULL) || !isStructKernelAddress(ts, MAX_TASK_STRUCT_SEARCH_SIZE) )
{
return (0);
}
//if we have the mm offset already then just check it
if (pPI->ts_mm != INV_OFFSET)
{
if ( get_target_ulong_at(env, ts + pPI->ts_mm) == 0)
{
bMMCheck = 1;
}
}
if (pPI->ts_real_parent != INV_OFFSET)
{
if (get_target_ulong_at(env, ts + pPI->ts_real_parent) == ts)
{
bRPCheck = 1;
}
}
if ( (bDoubleCheck && bMMCheck && bRPCheck)
|| (!bDoubleCheck && (bMMCheck || bRPCheck))
)
{
if (pPI->init_task_addr == INV_OFFSET)
{
pPI->init_task_addr = ts;
}
return (1);
}
return (0);
}
//To find the "comm" field, we look for the name of
// init_task which is "swapper" -- we don't check for "swapper/0" or anything else
gva_t findCommFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI)
{
target_ulong i = 0;
//char* temp = NULL; //not used yet, because we are using the int comparison instead
target_ulong temp2 = 0;
//char* strInit = "swapper";
uint32_t intSWAP = 0x70617773; //p, a, w, s
uint32_t intPER = 0x2f726570; ///, r, e, p
if (pPI == NULL)
{
return (INV_ADDR);
}
if (!isInitTask(env, ts, pPI, 0))
{
return (INV_ADDR);
}
//once again we are assuming that things are aligned
for (i = 0; i < MAX_TASK_STRUCT_SEARCH_SIZE; i+=sizeof(target_ulong))
{
temp2 = ts + i;
if (get_uint32_at(env, temp2) == intSWAP)
{
temp2 += 4; //move to the next item
if ((get_uint32_at(env, temp2) & 0x00FFFFFF) == (intPER & 0x00FFFFFF))
{
if (pPI->ts_comm == INV_OFFSET)
{
pPI->ts_comm = i;
}
return (ts+i);
}
}
}
return (INV_ADDR);
}
//The signature for thread_group is
// that thread_group comes after an array of pid_links
// and PID_links contains an hlist_node (which 2 pointers)
// followed by a pid pointer - this means 3 points
//So we are looking for 3 pointers followed by
// a list_head for the thread group
//Turns out the simple signature above doesn't work
// because there are too many points
// so we will use a signature for the
// hlist instead
//The uniqueness of an hlist (as opposed to the list)
// is that the second parameter is a pointer to a pointer (**prev)
//At the lowest level, this doesn't work either since
// it is hard to distinguish between a pointer to a pointer
// from a pointer - which means a list will look like an hlist
//Sigh - so instead we are going to use the cputime_t
// fields as part of the signature instead - this can be useful
// especially if we do this kind of test early on during
// the boot process
//So the new signature is list_head followed by 3 pointers
// followed by cputimes
//This one didn't work either, so going to just use
// a signature based on initTask
// which happens to have the whole array of pid_link
// pid_link consists of hlist, a basically another couple of pointers
// and a pointer to a pid (which seems to be the same value)
gva_t findThreadGroupFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI)
{
target_ulong i = 0;
if ( (pPI == NULL) )
{
return (INV_ADDR);
}
if (pPI->ts_group_leader == INV_OFFSET)
{
i = 0;
} //we can start from the group_leader as a shortcut
else
{
i = pPI->ts_group_leader;
}
if (!isInitTask(env, ts, pPI, 0))
{
return (INV_ADDR);
}
for ( ; i < MAX_TASK_STRUCT_SEARCH_SIZE; i+=sizeof(target_ulong))
{
/*
printk(KERN_INFO "%d === %"T_FMT"x, %"T_FMT"x, %"T_FMT"x, %"T_FMT"x, %"T_FMT"x, %d,%d,%d,%d,%d\n", i, get_target_ulong_at(env, ts + i),
get_target_ulong_at(env, ts + i + sizeof(target_ulong)),
get_target_ulong_at(env, ts+i+sizeof(target_ulong)+sizeof(target_ulong)),
get_target_ulong_at(env, ts+i+(sizeof(target_ulong)*3)),
get_target_ulong_at(env, ts+i+(sizeof(target_ulong)*4)),
(get_target_ulong_at(env, ts + i) == 0),
(get_target_ulong_at(env, ts + i + sizeof(target_ulong)) == 0),
isKernelAddress(get_target_ulong_at(env, ts+i+sizeof(target_ulong)+sizeof(target_ulong))),
isListHead(env, get_target_ulong_at(env, ts+i+(sizeof(target_ulong)*3))), //is a list head
(get_target_ulong_at(env, ts+i+(sizeof(target_ulong)*4)) == 0) //this is the entry for vfork_done ?
);
*/
//for init task the pid_link list should be all NULLS
/* This doesn't exactly work because of how different implementations
populate the pid_link array. So we will use the new signature below
if ( (get_target_ulong_at(env, ts + i) == 0)
&& (get_target_ulong_at(env, ts + i + sizeof(target_ulong)) == 0)
&& isKernelAddress(get_target_ulong_at(env, ts+i+sizeof(target_ulong)+sizeof(target_ulong)))
&& isListHead(env, get_target_ulong_at(env, ts+i+(sizeof(target_ulong)*3))) //is a list head
&& (get_target_ulong_at(env, ts+i+(sizeof(target_ulong)*3)+SIZEOF_LIST_HEAD) == 0) //this is the entry for vfork_done ?
)
*/
//Through some additional testing, I see that completion, set_child_tid and clear_child_tid should all be null - utime should be 0 too since it doens't use any time for "userspace" -- its a kernel process. Finally, stime - the system time, should be nonzero for startup, but a very small number since it doesn't get scheduled after the start.
// Given this fact, we are going to look for the list_head (which is thread_group) followed by 3 nulls. THe only trick here is that - in some architectures - the list head might be NULL pointers and in others it might be a pointer to itself. .
if ( ( isListHead(env, get_target_ulong_at(env, ts+i))
|| ( (get_target_ulong_at(env, ts+i) == 0) && (get_target_ulong_at(env, ts+i+sizeof(target_ulong)) == 0) ) )
&& (get_target_ulong_at(env, ts+i+SIZEOF_LIST_HEAD) == 0)
&& (get_target_ulong_at(env, ts+i+SIZEOF_LIST_HEAD+sizeof(target_ulong)) == 0)
&& (get_target_ulong_at(env, ts+i+SIZEOF_LIST_HEAD+(sizeof(target_ulong)*2)) == 0)
&& (get_target_ulong_at(env, ts+i+SIZEOF_LIST_HEAD+(sizeof(target_ulong)*3)) == 0) //utime = 0
&& (get_target_ulong_at(env, ts+i+SIZEOF_LIST_HEAD+(sizeof(target_ulong)*4)) != 0) //stime != 0
)
{
if ( pPI->ts_thread_group == INV_OFFSET )
{
pPI->ts_thread_group = i;
}
return (ts + i);
}
}
return (INV_ADDR);
}
//we find cred by searching backwards starting from comm
//The signature is that we have an array of list heads (which is for
// the cpu_timers
// followed by real_cred and cred (both of which are pointers)
// followed by stuff (in 2.6.32) and then comm
gva_t findCredFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI)
{
target_ulong i = 0;
if ((pPI == NULL) || (pPI->ts_comm == INV_OFFSET) )
{
return (INV_ADDR);
}
if (!isStructKernelAddress(ts, MAX_TASK_STRUCT_SEARCH_SIZE))
{
return (INV_ADDR);
}
//we start at 16 because of the list_head followed by
// the two pointers
for (i = (sizeof(target_ulong)*4); i < pPI->ts_comm; i+=sizeof(target_ulong))
{
if ( isListHead(env, get_target_ulong_at(env, ts + pPI->ts_comm - i))
&& isKernelAddress(get_target_ulong_at(env, ts + pPI->ts_comm - i + SIZEOF_LIST_HEAD))
&& isKernelAddress(get_target_ulong_at(env, ts + pPI->ts_comm - i + SIZEOF_LIST_HEAD + sizeof(target_ulong)))
)
{
if (pPI->ts_real_cred == INV_OFFSET)
{
pPI->ts_real_cred = pPI->ts_comm - i + SIZEOF_LIST_HEAD;
}
if (pPI->ts_cred == INV_OFFSET)
{
pPI->ts_cred = pPI->ts_comm - i + SIZEOF_LIST_HEAD + sizeof(target_ulong);
}
return (ts + pPI->ts_comm - i + SIZEOF_LIST_HEAD + sizeof(target_ulong));
}
}
return (INV_ADDR);
}
#if defined(TARGET_I386) || defined (TARGET_ARM)
#define STACK_CANARY_MASK 0xFFFF0000
#else
#define STACK_CANARY_MASK 0xFFFF0000FFFF0000
#endif
//pid and tgid are pretty much right on top of
// the real_parent, except for the case when a stack
// canary might be around. We will try to see
// if the canary is there - because canaries are supposed
// to be random - which is different from tgid and pid
// both of which are small numbers - so we try it this
// way
gva_t findPIDFromTaskStruct(CPUState * env, gva_t ts, ProcInfo* pPI)
{
target_ulong offset = 0;
target_ulong temp = 0;
if ( (pPI == NULL) || (pPI->ts_real_parent == INV_OFFSET) )
{
return (INV_ADDR);
}
if (!isStructKernelAddress(ts, MAX_TASK_STRUCT_SEARCH_SIZE))
{
return (INV_ADDR);
}
if ( pPI->ts_group_leader == INV_ADDR )
{
return (INV_ADDR);
}
ts = get_target_ulong_at(env, ts + pPI->ts_group_leader);
if (ts == INV_ADDR)
return (INV_ADDR);
//the signature is fairly simple - both pid and tgid are going to be the same
// as long as the task in question is a group_leader
//Now there is a potential for
// the stack canary to interfere - in which case we will
// check for the existence of the stack canary by looking at the values
//Since the canary is supposed to be random it should not be like pids
// which are expected to be low values
//Also notice that since pid_t is defined as
// an int - it should be 4 bytes. Thus, either the previous eight bytes
// look like a canary or not - see the masks that mask out the low bits
temp = get_target_ulong_at(env, ts+pPI->ts_real_parent-sizeof(target_ulong));
if (temp & STACK_CANARY_MASK)
{
offset = sizeof(target_ulong);
}
if (pPI->ts_pid == INV_OFFSET)
{
pPI->ts_pid = pPI->ts_real_parent - sizeof(target_pid_t)*2 - offset;
}
if (pPI->ts_tgid == INV_OFFSET)
{
pPI->ts_tgid = pPI->ts_real_parent - sizeof(target_pid_t) - offset;
}
//as it turns out there is also a potential alignment problem on 64 bit
// x86 - since the pointer for real_parent should be 8 bytes aligned
// and so should the stack_canary!
//To see if there is a problem - we check to see if the "expected"
// pid and tgid's match
if (get_uint32_at(env, ts + pPI->ts_pid) != get_uint32_at(env, ts + pPI->ts_tgid))
{
pPI->ts_pid -= 4;
pPI->ts_tgid -= 4;
}
if (get_uint32_at(env, ts + pPI->ts_pid) != get_uint32_at(env, ts + pPI->ts_tgid))
{
//printk(KERN_INFO "UH OH THEY ARE NOT THE SAME [%d], [%d]\n", get_uint32_at(env, pPI->ts_pid), get_uint32_at(env, pPI->ts_tgid));
}
return (ts + pPI->ts_pid);
}
//we should be able to populate all of the mm struct field at once
// since we are mostly interested in the vma, and the start stack, brk and etc
// areas
//So basically what we are going to rely on is the fact that
// we have 11 unsigned longs:
// startcode, endcode, startdata, enddata (4)
// startbrk, brk, startstack (3)
// argstart, argend, envstart, envend (4)
//Meaning we have a lot of fields with relative
// addresses in the same order as defined - except for brk
int isStartCodeInMM(CPUState * env, target_ulong* temp, target_ulong expectedStackStart)
{
if (temp == NULL)
{
return (0);
}
if (
(temp[0] > temp[1]) //startcode > endcode
|| (temp[1] > temp[2]) //endcode > startdata
|| (temp[2] > temp[3]) //startdata > enddata
|| (temp[3] > temp[4]) //enddata > startbrk
|| (temp[4] > temp[6]) //startbrk > startstack
|| (temp[6] > temp[7]) //startstack > argstart
|| (temp[7] > temp[8]) //argstart > argend
|| (temp[8] > temp[9]) //argend > envstart
|| (temp[9] > temp[10]) //envstart > envend
|| (temp[8] != temp[9]) //argend != envstart (the same?)
|| (temp[6] < expectedStackStart)
)
{
return (0);
}
/*
for (i = 0; i < 11; i++)
{
printk(KERN_INFO "CUR [%d] %16"T_FMT"x\n", i, ((target_ulong*)(¤t->mm->start_code))[i]);
}
for (i = 0; i < 11; i++)
{
printk(KERN_INFO "[%d] %16"T_FMT"x\n", i, temp[i]);
}
*/
return (1);
}
#define MM_TEMP_BUF_SIZE 100
int populate_mm_struct_offsets(CPUState * env, gva_t mm, ProcInfo* pPI)
{
static bool isOffsetPopulated = false;
if (isOffsetPopulated)
return (0);
target_ulong temp[MM_TEMP_BUF_SIZE + 11];
target_ulong* pTemp = temp;
target_ulong count = 0;
target_ulong numRead = 0;
if (pPI == NULL)
{
return (-1);
}
if (!isStructKernelAddress(mm, MAX_MM_STRUCT_SEARCH_SIZE))
{
return (-1);
}
//mmap always comes first it looks like
if (pPI->mm_mmap == INV_OFFSET)
{
pPI->mm_mmap = 0;
}
memset(temp, 0, sizeof(temp));
//grab a 11 ulong block
for (count = 0; count < (MAX_MM_STRUCT_SEARCH_SIZE / sizeof(target_ulong)); count++)
{
if ( (count % MM_TEMP_BUF_SIZE) == 0)
{
//if at the end of the buffer reset the pTemp pointer
// to the beginning
pTemp = temp;
//if we are at the beginning then grab 11 ulongs at a time
if (get_mem_at(env, mm+(sizeof(target_ulong)*count), pTemp, 11*sizeof(target_ulong)) != (11*sizeof(target_ulong)))