├── LICENSE
├── Makefile
├── README.md
├── modtracer.c
├── modtracer.png
└── modtracerv2.c
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 matheuz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | obj-m := modtracer.o
2 | CC = gcc -Wall
3 | KDIR := /lib/modules/$(shell uname -r)/build
4 | PWD := $(shell pwd)
5 |
6 | all:
7 | $(MAKE) -C $(KDIR) M=$(PWD) modules
8 |
9 | clean:
10 | $(MAKE) -C $(KDIR) M=$(PWD) clean
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ModTracer v1.0
2 | ModTracer Finds Hidden Linux Kernel Rootkits and then make visible again.
3 |
4 |

5 |
6 |
7 | Another way to make an LKM visible is using the imperius trick: https://github.com/MatheuZSecurity/Imperius
8 |
9 | Join in Rootkit Researchers Group
10 | https://discord.gg/66N5ZQppU7
11 |
--------------------------------------------------------------------------------
/modtracer.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | MODULE_LICENSE("GPL");
10 | MODULE_AUTHOR("MatheuZSec");
11 | MODULE_DESCRIPTION("Detect hidden LKM Rootkits and then make visible again.");
12 | MODULE_VERSION("1.0");
13 |
14 | struct module_region {
15 | unsigned long start;
16 | unsigned long end;
17 | };
18 |
19 | static struct module_region *module_regions = NULL;
20 | static int module_count = 0;
21 |
22 | static int cmp_func(const void *a, const void *b) {
23 | struct module_region *region_a = (struct module_region *)a;
24 | struct module_region *region_b = (struct module_region *)b;
25 | return (region_a->start > region_b->start) - (region_a->start < region_b->start);
26 | }
27 |
28 | static int gather_module_regions(void) {
29 | struct module *mod;
30 | struct module_region *new_regions;
31 | int i = 0;
32 |
33 | list_for_each_entry(mod, THIS_MODULE->list.prev, list) {
34 | new_regions = krealloc(module_regions, (module_count + 1) * sizeof(*module_regions), GFP_KERNEL);
35 | if (!new_regions) {
36 | pr_err("Memory allocation failed for module regions\n");
37 | return -ENOMEM;
38 | }
39 |
40 | module_regions = new_regions;
41 |
42 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
43 | module_regions[module_count].start = (unsigned long)mod->mem->base;
44 | module_regions[module_count].end = (unsigned long)mod->mem->base + mod->mem->size;
45 | #else
46 | module_regions[module_count].start = (unsigned long)mod->core_layout.base;
47 | module_regions[module_count].end = (unsigned long)mod->core_layout.base + mod->core_layout.size;
48 | #endif
49 |
50 | module_count++;
51 | }
52 |
53 | // Sort the regions by their start address
54 | sort(module_regions, module_count, sizeof(struct module_region), cmp_func, NULL);
55 |
56 | return 0;
57 | }
58 |
59 | static void modtracer_memory_gaps(void) {
60 | unsigned long addr, value;
61 | struct module *mod;
62 | size_t ptr_size = sizeof(void *);
63 | int i;
64 |
65 | for (i = 0; i < module_count - 1; i++) {
66 | for (addr = module_regions[i].end; addr < module_regions[i + 1].start; addr += ptr_size) {
67 | if (copy_from_kernel_nofault(&value, (void *)addr, sizeof(value)) != 0)
68 | continue;
69 |
70 | if (value == (unsigned long)LIST_POISON1) {
71 | if (copy_from_kernel_nofault(&value, (void *)(addr + ptr_size), sizeof(value)) != 0)
72 | continue;
73 |
74 | if (value == (unsigned long)LIST_POISON2) {
75 | mod = (struct module *)(addr - ptr_size);
76 | pr_info("Hidden LKM Rootkit detected: %s! Check lsmod and then remove it", mod->name);
77 | list_add(&mod->list, THIS_MODULE->list.prev);
78 | break;
79 | }
80 | }
81 | }
82 | }
83 | }
84 |
85 | static int __init modtracer_init(void) {
86 | pr_info("ModTracer Loaded...\n");
87 |
88 | if (gather_module_regions() < 0) {
89 | return -ENOMEM;
90 | }
91 |
92 | modtracer_memory_gaps();
93 |
94 | pr_info("ModTracer completed!\n");
95 |
96 | return 0;
97 | }
98 |
99 | static void __exit modtracer_exit(void) {
100 | kfree(module_regions);
101 | pr_info("ModTracer Unloaded!\n");
102 | }
103 |
104 | module_init(modtracer_init);
105 | module_exit(modtracer_exit);
106 |
--------------------------------------------------------------------------------
/modtracer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MatheuZSecurity/ModTracer/462b224162052f7520677b889bc7bef90e0d29a5/modtracer.png
--------------------------------------------------------------------------------
/modtracerv2.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | MODULE_LICENSE("GPL");
10 | MODULE_AUTHOR("MatheuZSec");
11 | MODULE_DESCRIPTION("Detect hidden LKM Rootkits and then make visible again.");
12 | MODULE_VERSION("1.0");
13 |
14 | struct module_region {
15 | unsigned long start;
16 | unsigned long end;
17 | };
18 |
19 | static struct module_region *module_regions = NULL;
20 | static int module_count = 0;
21 |
22 | static int cmp_func(const void *a, const void *b) {
23 | struct module_region *region_a = (struct module_region *)a;
24 | struct module_region *region_b = (struct module_region *)b;
25 | return (region_a->start > region_b->start) - (region_a->start < region_b->start);
26 | }
27 |
28 | static int gather_module_regions(void) {
29 | struct module *mod;
30 | struct module_region *new_regions;
31 | int i = 0;
32 |
33 | list_for_each_entry(mod, THIS_MODULE->list.prev, list) {
34 | new_regions = krealloc(module_regions, (module_count + 1) * sizeof(*module_regions), GFP_KERNEL);
35 | if (!new_regions) {
36 | pr_err("Memory allocation failed for module regions\n");
37 | return -ENOMEM;
38 | }
39 |
40 | module_regions = new_regions;
41 |
42 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
43 | module_regions[module_count].start = (unsigned long)mod->mem->base;
44 | module_regions[module_count].end = (unsigned long)mod->mem->base + mod->mem->size;
45 | #else
46 | module_regions[module_count].start = (unsigned long)mod->core_layout.base;
47 | module_regions[module_count].end = (unsigned long)mod->core_layout.base + mod->core_layout.size;
48 | #endif
49 |
50 | module_count++;
51 | }
52 |
53 | // Sort the regions by their start address
54 | sort(module_regions, module_count, sizeof(struct module_region), cmp_func, NULL);
55 |
56 | return 0;
57 | }
58 |
59 | static void modtracer_memory_gaps(void) {
60 | unsigned long addr, value;
61 | struct module *mod;
62 | size_t ptr_size = sizeof(void *);
63 | int i;
64 |
65 | for (i = 0; i < module_count - 1; i++) {
66 | for (addr = module_regions[i].end; addr < module_regions[i + 1].start; addr += ptr_size) {
67 | if (copy_from_kernel_nofault(&value, (void *)addr, sizeof(value)) != 0)
68 | continue;
69 |
70 | if (value == (unsigned long)0xdeadbeef) {
71 | if (copy_from_kernel_nofault(&value, (void *)(addr + ptr_size), sizeof(value)) != 0)
72 | continue;
73 |
74 | if (value == (unsigned long)0xdeadbeef) {
75 | mod = (struct module *)(addr - offsetof(struct module, list));
76 | pr_info("Hidden LKM Rootkit detected: %s! Check lsmod and then remove it", mod->name);
77 | list_add(&mod->list, THIS_MODULE->list.prev);
78 | break;
79 | }
80 | }
81 | }
82 | }
83 | }
84 |
85 | static int __init modtracer_init(void) {
86 | pr_info("ModTracer Loaded...\n");
87 |
88 | if (gather_module_regions() < 0) {
89 | return -ENOMEM;
90 | }
91 |
92 | modtracer_memory_gaps();
93 |
94 | pr_info("ModTracer completed!\n");
95 |
96 | return 0;
97 | }
98 |
99 | static void __exit modtracer_exit(void) {
100 | kfree(module_regions);
101 | pr_info("ModTracer Unloaded!\n");
102 | }
103 |
104 | module_init(modtracer_init);
105 | module_exit(modtracer_exit);
106 |
--------------------------------------------------------------------------------