mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-23 02:05:57 +01:00
virt_to_phys_user
This commit is contained in:
@@ -74,7 +74,7 @@ static int open(struct inode *inode, struct file *filp)
|
||||
|
||||
pr_info("open\n");
|
||||
info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
|
||||
pr_info("virt_to_phys = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info->data));
|
||||
pr_info("virt_to_phys = 0x%llx\n", (unsigned long long)virt_to_phys((void *)info));
|
||||
info->data = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
memcpy(info->data, "asdf", BUFFER_SIZE);
|
||||
filp->private_data = info;
|
||||
|
||||
74
kernel_module/user/common.h
Normal file
74
kernel_module/user/common.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <fcntl.h> /* open */
|
||||
#include <stdint.h> /* uint64_t */
|
||||
#include <stdlib.h> /* size_t */
|
||||
#include <unistd.h> /* pread, sysconf */
|
||||
|
||||
typedef struct {
|
||||
uint64_t pfn : 54;
|
||||
unsigned int soft_dirty : 1;
|
||||
unsigned int file_page : 1;
|
||||
unsigned int swapped : 1;
|
||||
unsigned int present : 1;
|
||||
} PagemapEntry;
|
||||
|
||||
/* Parse the pagemap entry for the given virtual address.
|
||||
*
|
||||
* @param[out] entry the parsed entry
|
||||
* @param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file
|
||||
* @param[in] vaddr virtual address to get entry for
|
||||
* @return 0 for success, 1 for failure
|
||||
*/
|
||||
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
|
||||
{
|
||||
size_t nread;
|
||||
ssize_t ret;
|
||||
uint64_t data;
|
||||
|
||||
nread = 0;
|
||||
while (nread < sizeof(data)) {
|
||||
ret = pread(pagemap_fd, &data, sizeof(data),
|
||||
(vaddr / sysconf(_SC_PAGE_SIZE)) * sizeof(data) + nread);
|
||||
nread += ret;
|
||||
if (ret <= 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
entry->pfn = data & (((uint64_t)1 << 54) - 1);
|
||||
entry->soft_dirty = (data >> 54) & 1;
|
||||
entry->file_page = (data >> 61) & 1;
|
||||
entry->swapped = (data >> 62) & 1;
|
||||
entry->present = (data >> 63) & 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert the given virtual address to physical using /proc/PID/pagemap.
|
||||
*
|
||||
* @param[out] paddr physical address
|
||||
* @param[in] pid process to convert for
|
||||
* @param[in] vaddr virtual address to get entry for
|
||||
* @return 0 for success, 1 for failure
|
||||
*/
|
||||
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
|
||||
{
|
||||
char pagemap_file[BUFSIZ];
|
||||
int pagemap_fd;
|
||||
|
||||
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
|
||||
pagemap_fd = open(pagemap_file, O_RDONLY);
|
||||
if (pagemap_fd < 0) {
|
||||
return 1;
|
||||
}
|
||||
PagemapEntry entry;
|
||||
if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
|
||||
return 1;
|
||||
}
|
||||
close(pagemap_fd);
|
||||
*paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,10 +2,13 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h> /* uintmax_t */
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h> /* sysconf */
|
||||
|
||||
#include "common.h" /* virt_to_phys_user */
|
||||
|
||||
enum { BUFFER_SIZE = 4 };
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -14,6 +17,7 @@ int main(int argc, char **argv)
|
||||
long page_size;
|
||||
char *address1, *address2;
|
||||
char buf[BUFFER_SIZE];
|
||||
uintptr_t paddr;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s <mmap_file>\n", argv[0]);
|
||||
@@ -54,6 +58,13 @@ int main(int argc, char **argv)
|
||||
/* Also modified. So both virtual addresses point to the same physical address. */
|
||||
assert(!strcmp(address2, "qwer"));
|
||||
|
||||
/* Check that the physical addresses are the same.
|
||||
* They are, but TODO why virt_to_phys on kernel gives a different value? */
|
||||
assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address1));
|
||||
printf("paddr1 = 0x%jx\n", (uintmax_t)paddr);
|
||||
assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address2));
|
||||
printf("paddr2 = 0x%jx\n", (uintmax_t)paddr);
|
||||
|
||||
/* Check that modifications made from userland are also visible from the kernel. */
|
||||
read(fd, buf, BUFFER_SIZE);
|
||||
assert(!memcmp(buf, "qwer", BUFFER_SIZE));
|
||||
|
||||
@@ -3,7 +3,8 @@ Only tested in x86_64.
|
||||
|
||||
Adapted from: https://github.com/dwks/pagemap/blob/8a25747bc79d6080c8b94eac80807a4dceeda57a/pagemap2.c
|
||||
|
||||
https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141
|
||||
- https://stackoverflow.com/questions/17021214/how-to-decode-proc-pid-pagemap-entries-in-linux/45126141#45126141
|
||||
- https://stackoverflow.com/questions/5748492/is-there-any-api-for-determining-the-physical-address-from-virtual-address-in-li
|
||||
|
||||
Dump the page map of a given process PID.
|
||||
|
||||
@@ -13,106 +14,79 @@ Data sources: /proc/PIC/{map,pagemap}
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef struct {
|
||||
uint64_t phys : 54;
|
||||
int soft_dirty : 1;
|
||||
int padding : 4;
|
||||
int padding : 1;
|
||||
|
||||
* Bit 61 page is file-page or shared-anon (since 3.5)
|
||||
* Bit 62 page swapped
|
||||
* Bit 63 page present
|
||||
} PagemapEntry;
|
||||
|
||||
|
||||
int parse_pagemap(PagemapEntry &entry, int fd, size_t offset)
|
||||
{
|
||||
if (pread(pagemap, &data, ) != sizeof(data)) {
|
||||
perror("pread");
|
||||
break;
|
||||
}
|
||||
|
||||
data & 0x7fffffffffffff,
|
||||
(data >> 55) & 1,
|
||||
(data >> 61) & 1,
|
||||
(data >> 62) & 1,
|
||||
(data >> 63) & 1,
|
||||
|
||||
}
|
||||
#include "common.h" /* pagemap_get_entry */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char buffer[BUFSIZ];
|
||||
char maps_file[BUFSIZ];
|
||||
char pagemap_file[BUFSIZ];
|
||||
int maps;
|
||||
int maps_fd;
|
||||
int offset = 0;
|
||||
int pagemap;
|
||||
long page_size;
|
||||
int pagemap_fd;
|
||||
pid_t pid;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("Usage: %s pid\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pid_t pid = (pid_t)strtoul(argv[1], NULL, 0);
|
||||
snprintf(maps_file, sizeof(maps_file), "/proc/%lu/maps", (unsigned long)pid);
|
||||
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%lu/pagemap", (unsigned long)pid);
|
||||
maps = open(maps_file, O_RDONLY);
|
||||
if (maps < 0) {
|
||||
pid = strtoull(argv[1], NULL, 0);
|
||||
snprintf(maps_file, sizeof(maps_file), "/proc/%ju/maps", (uintmax_t)pid);
|
||||
snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
|
||||
maps_fd = open(maps_file, O_RDONLY);
|
||||
if (maps_fd < 0) {
|
||||
perror("open maps");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pagemap = open(pagemap_file, O_RDONLY);
|
||||
if (pagemap < 0) {
|
||||
pagemap_fd = open(pagemap_file, O_RDONLY);
|
||||
if (pagemap_fd < 0) {
|
||||
perror("open pagemap");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
printf("addr pfn soft-dirty file/shared swapped present library\n");
|
||||
for (;;) {
|
||||
ssize_t length = read(maps, buffer + offset, sizeof buffer - offset);
|
||||
ssize_t length = read(maps_fd, buffer + offset, sizeof buffer - offset);
|
||||
if (length <= 0) break;
|
||||
length += offset;
|
||||
for (size_t i = offset; i < (size_t)length; i++) {
|
||||
unsigned long low = 0, high = 0;
|
||||
uintptr_t low = 0, high = 0;
|
||||
if (buffer[i] == '\n' && i) {
|
||||
const char *lib_name;
|
||||
size_t y;
|
||||
/* Parse a line from maps. Each line contains a range that contains many pages. */
|
||||
{
|
||||
size_t x = i - 1;
|
||||
while(x && buffer[x] != '\n') x --;
|
||||
while (x && buffer[x] != '\n') x--;
|
||||
if (buffer[x] == '\n') x++;
|
||||
size_t beginning = x;
|
||||
while(buffer[x] != '-' && x < sizeof buffer) {
|
||||
while (buffer[x] != '-' && x < sizeof buffer) {
|
||||
char c = buffer[x++];
|
||||
low *= 16;
|
||||
if (c >= '0' && c <= '9') {
|
||||
low += c - '0';
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f') {
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
low += c - 'a' + 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(buffer[x] != '-' && x < sizeof buffer) x++;
|
||||
while (buffer[x] != '-' && x < sizeof buffer) x++;
|
||||
if (buffer[x] == '-') x++;
|
||||
while(buffer[x] != ' ' && x < sizeof buffer) {
|
||||
while (buffer[x] != ' ' && x < sizeof buffer) {
|
||||
char c = buffer[x++];
|
||||
high *= 16;
|
||||
if (c >= '0' && c <= '9') {
|
||||
high += c - '0';
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f') {
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
high += c - 'a' + 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
lib_name = 0;
|
||||
for (int field = 0; field < 4; field++) {
|
||||
@@ -127,27 +101,27 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
/* Get info about all pages in this page range with pagemap. */
|
||||
{
|
||||
unsigned long data;
|
||||
PagemapEntry entry;
|
||||
for (unsigned long i = low; i < high; i += page_size) {
|
||||
unsigned long index = (i / page_size) * sizeof(data);
|
||||
parse_pagemap(&entry, pagemap, sizeof(data) * index);
|
||||
printf("%lx %lx %d %d %d %d %s\n",
|
||||
i,
|
||||
data & 0x7fffffffffffff,
|
||||
(data >> 55) & 1,
|
||||
(data >> 61) & 1,
|
||||
(data >> 62) & 1,
|
||||
(data >> 63) & 1,
|
||||
lib_name
|
||||
);
|
||||
for (uintptr_t addr = low; addr < high; addr += sysconf(_SC_PAGE_SIZE)) {
|
||||
/* TODO always fails for the last page (vsyscall), why? pread returns 0. */
|
||||
if (!pagemap_get_entry(&entry, pagemap_fd, addr)) {
|
||||
printf("%jx %jx %u %u %u %u %s\n",
|
||||
(uintmax_t)addr,
|
||||
(uintmax_t)entry.pfn,
|
||||
entry.soft_dirty,
|
||||
entry.file_page,
|
||||
entry.swapped,
|
||||
entry.present,
|
||||
lib_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer[y] = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
close(maps);
|
||||
close(pagemap);
|
||||
return 0;
|
||||
close(maps_fd);
|
||||
close(pagemap_fd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
29
kernel_module/user/virt_to_phys_user.c
Normal file
29
kernel_module/user/virt_to_phys_user.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Convert a virtual address to physical for a given process PID using /proc/PID/pagemap.
|
||||
|
||||
https://stackoverflow.com/questions/5748492/is-there-any-api-for-determining-the-physical-address-from-virtual-address-in-li/45128487#45128487
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* printf */
|
||||
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE, strtoull */
|
||||
|
||||
#include "common.h" /* virt_to_phys_user */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
uintptr_t vaddr, paddr;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("Usage: %s pid vaddr\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pid = strtoull(argv[1], NULL, 0);
|
||||
vaddr = strtoull(argv[2], NULL, 0);
|
||||
if (virt_to_phys_user(&paddr, pid, vaddr)) {
|
||||
fprintf(stderr, "error: virt_to_phys_user\n");
|
||||
return EXIT_FAILURE;
|
||||
};
|
||||
printf("0x%jx\n", (uintmax_t)paddr);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user