Automatically load program load offset

This commit is contained in:
kamkow1
2025-03-09 17:49:38 +01:00
parent bb8eafbe32
commit 3cd61ab871
4 changed files with 436 additions and 1 deletions

View File

@@ -1,7 +1,7 @@
CC=gcc
CFLAGS=-MD -MP -ggdb -I./mujs
LDFLAGS=-lm
SRCS=debugus.c linenoise.c hash.c
SRCS=debugus.c linenoise.c hash.c pmparser.c
OBJS=$(patsubst %.c,%.o,$(SRCS))
DEPS=$(patsubst %.c,%.d,$(SRCS))

View File

@@ -14,6 +14,7 @@
#include "linenoise.h"
#include "mujs.h"
#include "hash.h"
#include "pmparser.h"
#define LOG_ERR(fmt, ...) fprintf(stderr, "Error: " fmt, ##__VA_ARGS__)
#define LOG_INF(fmt, ...) fprintf(stdout, "Info: " fmt, ##__VA_ARGS__)
@@ -33,6 +34,7 @@
typedef struct {
pid_t pid;
pid_t proc_pid;
uintptr_t addr;
bool enabled;
uint8_t data;
@@ -180,12 +182,32 @@ void dbg_init_js(Dbg *dbg)
#undef make_js_func
}
void dbg_init_load_offset(Dbg *dbg)
{
procmaps_iterator maps_iter = {0};
procmaps_error_t parser_err = PROCMAPS_SUCCESS;
parser_err = pmparser_parse(dbg->pid, &maps_iter);
if (parser_err) {
LOG_ERR("Failed to parse /proc/%d/maps (%d)\n", dbg->pid, (int)parser_err);
return;
}
// We only need the first one
procmaps_struct *mem_region = pmparser_next(&maps_iter);
dbg->program_load_offset = (uintptr_t)mem_region->addr_start;
pmparser_free(&maps_iter);
}
void dbg_init(Dbg *dbg, const char *file, pid_t pid)
{
memset(dbg, 0, sizeof(*dbg));
dbg->file = file;
dbg->pid = pid;
dbg_init_js(dbg);
dbg_init_load_offset(dbg);
hashtable_init(&dbg->brks, MAX_BRKS);
}

301
pmparser.c Normal file
View File

@@ -0,0 +1,301 @@
/*
@Author : ouadev
@date : December 2015
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. No representations are made about the suitability of this
software for any purpose. It is provided "as is" without express or
implied warranty.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pmparser.h"
// maximum line length in a procmaps file
#define PROCMAPS_LINE_INIT_LENGTH (300)
// maximum length of the path of a maps file : /proc/[pid]/maps
#define PROCMAPS_MAPS_FILE_PATH_MAX_LENGTH 30
// maximum token size while parsing the proc/pid/maps
#define PROCMAPS_LINE_TOKEN_MAX_LEN 100
/**
* pmparser_parse_line
* @description internal usage
*/
static void pmparser_parse_line(char *buf, procmaps_struct *mem_reg);
/**
* @brief Copy into dest_ptr the string from src_ptr to the first occurence of delimiter
*/
static char *pmparser_helper_extract(char *src_ptr, const char *delimiter, char *dest_ptr);
/**
* @brief Main function to parse process memory
*
* @param pid process ID
* @param maps_it output : the memory region iterator over the chained list, it should only be read when return is 0
* @return procmaps_error_t outcome of the function
*/
procmaps_error_t pmparser_parse(int pid, procmaps_iterator *maps_it)
{
char maps_path[PROCMAPS_MAPS_FILE_PATH_MAX_LENGTH];
size_t line_len = 0;
char *line_ptr = NULL;
procmaps_struct *mem_reg = NULL;
procmaps_struct *tail_node = NULL;
procmaps_struct *head_node = NULL;
size_t node_count = 0;
if (pid >= 0)
{
snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", pid);
}
else
{
sprintf(maps_path, "/proc/self/maps");
}
FILE *file = fopen(maps_path, "r");
if (!file)
{
return PROCMAPS_ERROR_OPEN_MAPS_FILE;
}
// scan maps file line by line
line_len = PROCMAPS_LINE_INIT_LENGTH;
if ((line_ptr = (char *)malloc(line_len)) == NULL)
{
fclose(file);
return PROCMAPS_ERROR_MALLOC_FAIL;
}
while (1)
{
int read = getline(&line_ptr, &line_len, file);
if (read == -1)
{
if (!feof(file))
{
return PROCMAPS_ERROR_READ_MAPS_FILE;
}
else
{
// end of file occured while no characters have been read
break;
}
}
// allocate a node
mem_reg = (procmaps_struct *)malloc(sizeof(procmaps_struct));
// fill the node
pmparser_parse_line(line_ptr, mem_reg);
mem_reg->next = NULL;
// Attach the node
if (tail_node == NULL)
{
head_node = tail_node = mem_reg;
}
else
{
tail_node->next = mem_reg;
tail_node = mem_reg;
}
node_count++;
}
// close file
fclose(file);
free(line_ptr);
// set iterator
maps_it->head = maps_it->current = head_node;
maps_it->count = node_count;
return PROCMAPS_SUCCESS;
}
/**
* @brief move the iterator to the next memory region
*
* @param p_procmaps_it
* @return procmaps_struct*
*/
procmaps_struct *pmparser_next(procmaps_iterator *p_procmaps_it)
{
if (p_procmaps_it->current == NULL)
return NULL;
procmaps_struct *p_current = p_procmaps_it->current;
p_procmaps_it->current = p_procmaps_it->current->next;
return p_current;
}
/**
* @brief free the parser data
*
* @param p_procmaps_it
*/
void pmparser_free(procmaps_iterator *p_procmaps_it)
{
procmaps_struct *cursor = p_procmaps_it->head;
procmaps_struct *next = NULL;
if (p_procmaps_it->head == NULL)
return;
while (cursor != NULL)
{
next = cursor->next;
free(cursor->pathname);
free(cursor);
cursor = next;
}
memset(p_procmaps_it, 0x00, sizeof(procmaps_iterator));
}
static char *pmparser_helper_extract(char *src_ptr, const char *delimiter, char *dest_ptr)
{
char *p_separator = NULL;
size_t copy_len = 0;
p_separator = strstr(src_ptr, delimiter);
copy_len = (p_separator - src_ptr);
memcpy(dest_ptr, src_ptr, copy_len);
dest_ptr[copy_len] = 0x00;
return p_separator;
}
static void pmparser_parse_line(char *buf, procmaps_struct *mem_reg)
{
char token[PROCMAPS_LINE_TOKEN_MAX_LEN];
size_t pathname_len = 0;
char *p_cursor = buf;
// addr1
p_cursor = pmparser_helper_extract(p_cursor, "-", token);
p_cursor++;
sscanf(token, "%lx", (long unsigned *)&mem_reg->addr_start);
// addr2
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
p_cursor++;
sscanf(token, "%lx", (long unsigned *)&mem_reg->addr_end);
// region size
mem_reg->length = (unsigned long)((char*)mem_reg->addr_end - (char*)mem_reg->addr_start);
// perm
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
p_cursor++;
mem_reg->is_r = (token[0] == 'r');
mem_reg->is_w = (token[1] == 'w');
mem_reg->is_x = (token[2] == 'x');
mem_reg->is_p = (token[3] == 'p');
// offset
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
p_cursor++;
sscanf(token, "%lx", &mem_reg->offset);
// dev
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
p_cursor++;
sscanf(token, "%u:%u", &mem_reg->dev_major, &mem_reg->dev_minor);
// inode
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
p_cursor++;
sscanf(token, "%llu", &mem_reg->inode);
// pathname
// find the start of the pathname
while (*p_cursor == '\t' || *p_cursor == ' ')
p_cursor++;
// calculate its size
char *ptr_sz = p_cursor;
while (*ptr_sz != '\n')
{
ptr_sz++;
}
pathname_len = (ptr_sz - p_cursor);
// copy it
mem_reg->pathname = (char *)malloc(pathname_len * sizeof(char) + 1);
memcpy(mem_reg->pathname, p_cursor, pathname_len);
mem_reg->pathname[pathname_len] = 0x00;
// Pathname decoding
if (mem_reg->pathname[0] == 0x00)
{
// empty path name
mem_reg->map_type = PROCMAPS_MAP_ANON_MMAPS;
}
else if (strncmp(mem_reg->pathname, "[stack]", 7) == 0)
{
// mapping backed by main thread stack
mem_reg->map_type = PROCMAPS_MAP_STACK;
}
else if (strncmp(mem_reg->pathname, "[stack:", 7) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_STACK_TID;
}
else if (strncmp(mem_reg->pathname, "[vdso]", 6) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_VDSO;
}
else if (strncmp(mem_reg->pathname, "[heap]", 6) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_HEAP;
}
else if (strncmp(mem_reg->pathname, "[anon:", 6) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_ANON_PRIV;
pmparser_helper_extract(mem_reg->pathname + 6, "]", token);
strncpy(mem_reg->map_anon_name, token, MAPPING_ANON_NAME_MAX_LEN);
}
else if (strncmp(mem_reg->pathname, "[anon_shmem:", 12) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_ANON_SHMEM;
pmparser_helper_extract(mem_reg->pathname + 12, "]", token);
strncpy(mem_reg->map_anon_name, token, MAPPING_ANON_NAME_MAX_LEN);
}
else if (strncmp(mem_reg->pathname, "[vvar]", 6) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_VVAR;
}
else if (strncmp(mem_reg->pathname, "[vsyscall]", 10) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_VSYSCALL;
}
else if (strncmp(mem_reg->pathname, "[", 1) == 0)
{
mem_reg->map_type = PROCMAPS_MAP_OTHER;
}
else
{
// file backed mapping then
mem_reg->map_type = PROCMAPS_MAP_FILE;
// is the file deleted ?
// file_deleted
if (memcmp(mem_reg->pathname + strlen(mem_reg->pathname) - 9, "(deleted)", 9) == 0)
{
mem_reg->file_deleted = 1;
}
else
{
mem_reg->file_deleted = 0;
}
}
}

112
pmparser.h Normal file
View File

@@ -0,0 +1,112 @@
/*
@Author : ouadev
@date : December 2015
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. No representations are made about the suitability of this
software for any purpose. It is provided "as is" without express or
implied warranty.
*/
#ifndef H_PMPARSER
#define H_PMPARSER
// Documentation link: https://man7.org/linux/man-pages/man5/proc_pid_maps.5.html
// maximum length of the name of an anonymous mapping
#define MAPPING_ANON_NAME_MAX_LEN 80
/**
* @brief Type of a memory's region mapping.
*
*/
typedef enum
{
PROCMAPS_MAP_FILE,
PROCMAPS_MAP_STACK,
PROCMAPS_MAP_STACK_TID,
PROCMAPS_MAP_VDSO,
PROCMAPS_MAP_VVAR,
PROCMAPS_MAP_VSYSCALL,
PROCMAPS_MAP_HEAP,
PROCMAPS_MAP_ANON_PRIV,
PROCMAPS_MAP_ANON_SHMEM,
PROCMAPS_MAP_ANON_MMAPS,
PROCMAPS_MAP_OTHER,
} procmaps_map_type;
/**
* procmaps_struct
* @desc hold all the information about an area in the process's VM
*/
typedef struct procmaps_struct
{
void *addr_start; //< start address of the area
void *addr_end; //< end address
size_t length; //< size of the range
short is_r;
short is_w;
short is_x;
short is_p;
size_t offset; //< offset
unsigned int dev_major;
unsigned int dev_minor;
unsigned long long inode; //< inode of the file that backs the area
char *pathname; //< the path of the file that backs the area ( dynamically allocated)
procmaps_map_type map_type;
char map_anon_name[MAPPING_ANON_NAME_MAX_LEN + 1]; //< name of the anonymous mapping in case map_type is an anon mapping
short file_deleted; //< whether the file backing the mapping was deleted
// chained list
struct procmaps_struct *next; //<handler of the chained list
} procmaps_struct;
/**
* @brief procmaps error type
*
*/
typedef enum procmaps_error
{
PROCMAPS_SUCCESS = 0,
PROCMAPS_ERROR_OPEN_MAPS_FILE,
PROCMAPS_ERROR_READ_MAPS_FILE,
PROCMAPS_ERROR_MALLOC_FAIL,
} procmaps_error_t;
/**
* procmaps_iterator
* @desc holds iterating information
*/
typedef struct procmaps_iterator
{
procmaps_struct *head;
procmaps_struct *current;
size_t count;
} procmaps_iterator;
/**
* @brief Main function to parse process memory
* @param pid process ID
* @param maps_it output : the memory region iterator over the chained list, t should only be read when return is 0.
* @return procmaps_error_t outcome of the function
*/
procmaps_error_t pmparser_parse(int pid, procmaps_iterator *maps_it);
/**
* pmparser_next
* @description move between areas
* @param p_procmaps_it the iterator to move on step in the chained list
* @return a procmaps structure filled with information about this VM area
*/
procmaps_struct *pmparser_next(procmaps_iterator *p_procmaps_it);
/**
* pmparser_free
* @description should be called at the end to free the resources
* @param p_procmaps_it the iterator structure returned by pmparser_parse
*/
void pmparser_free(procmaps_iterator *p_procmaps_it);
#endif