Get various addresses and informations that can be useful for qemu to generate dumps, and provide enough details for KASLR kernels to be exploitable by tools like crash.
phys-base: the physical address where the kernel is loaded vmcoreinfo: can be used to read the guest vmcoreinfo ELF note _text: can be used to compute the kaslr offset (not needed when vmcoreinfo) Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- qga/main.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/qga/main.c b/qga/main.c index beff041340..1c39895baa 100644 --- a/qga/main.c +++ b/qga/main.c @@ -65,6 +65,14 @@ typedef struct GAPersistentState { int64_t fd_counter; } GAPersistentState; +typedef struct VMDumpInfo { + bool has_phys_base; + uint64_t phys_base; + bool has_text; + uint64_t text; + char *vmcoreinfo; +} VMDumpInfo; + struct GAState { JSONMessageParser parser; GMainLoop *main_loop; @@ -90,6 +98,7 @@ struct GAState { #endif gchar *pstate_filepath; GAPersistentState pstate; + VMDumpInfo vmdump; }; struct GAState *ga_state; @@ -1357,6 +1366,128 @@ static int run_agent(GAState *s, GAConfig *config) return EXIT_SUCCESS; } +#define __START_KERNEL_map 0xffffffff80000000UL + +static void update_vmdump_info(GAState *s) +{ +#ifndef _WIN32 + char *vmcoreinfo = NULL; + char *iomem = NULL; + char *kallsyms = NULL; + gsize len; + GError *err = NULL; +#ifdef __x86_64__ + guint64 kernel_code_start, text_start, phys_base; + char *tmp, *line, *eol; + + if (!g_file_get_contents("/proc/iomem", &iomem, &len, &err)) { + g_critical("Failed to read /proc/iomem: %s", err->message); + g_clear_error(&err); + goto end; + } + if (!iomem[len] == '\0') { + g_critical("iomem is not null-terminated"); + goto end; + } + + for (line = iomem; line != NULL; line = eol ? eol + 1 : NULL) { + eol = strstr(line, "\n"); + if (eol) { + *eol = '\0'; + } + tmp = strstr(line, " : Kernel code"); + if (!tmp || *(tmp + 14) != '\0') { + continue; + } else { + break; + } + } + + if (line == NULL) { + g_critical("failed to find kernel_code_start"); + goto end; + } else { + tmp = strstr(line, "-"); + if (!tmp) { + g_critical("no -"); + goto end; + } + *tmp = '\0'; + kernel_code_start = g_ascii_strtoull(g_strstrip(line), &tmp, 16); + if (*tmp != '\0') { + g_critical("failed to parse hex value"); + goto end; + } + } + + if (!g_file_get_contents("/proc/kallsyms", &kallsyms, &len, &err)) { + g_critical("Failed to read /proc/kallsyms: %s", err->message); + g_clear_error(&err); + goto end; + } + if (!kallsyms[len] == '\0') { + g_critical("iomem is not null-terminated"); + goto end; + } + + for (line = kallsyms; line != NULL; line = eol ? eol + 1 : NULL) { + eol = strstr(line, "\n"); + if (eol) { + *eol = '\0'; + } + tmp = strstr(line, " T _text"); + if (!tmp || *(tmp + 8) != '\0') { + continue; + } else { + *tmp = '\0'; + break; + } + } + + if (line == NULL) { + g_critical("failed to find _text"); + goto end; + } else { + text_start = g_ascii_strtoull(g_strstrip(line), &tmp, 16); + if (*tmp != '\0') { + g_critical("failed to parse hex value"); + goto end; + } + } + + phys_base = kernel_code_start - (text_start - __START_KERNEL_map); + g_debug("_text: %lx, kernel_code_start: %lx", + text_start, kernel_code_start); + g_debug("-> phys_base: %lx", phys_base); + g_debug("vmcoreinfo: %s", vmcoreinfo); + + s->vmdump.has_phys_base = true; + s->vmdump.phys_base = phys_base; + s->vmdump.has_text = true; + s->vmdump.text = text_start; +#endif /* x86_64 */ + + if (!g_file_get_contents("/sys/kernel/vmcoreinfo", + &vmcoreinfo, &len, &err)) { + g_critical("Failed to read vmcoreinfo: %s", err->message); + g_clear_error(&err); + goto end; + } + if (!vmcoreinfo[len] == '\0') { + g_critical("vmcoreinfo is not null-terminated"); + goto end; + } + + s->vmdump.vmcoreinfo = vmcoreinfo; + vmcoreinfo = NULL; + +end: + g_free(vmcoreinfo); + g_free(kallsyms); + g_free(iomem); +#endif +} + int main(int argc, char **argv) { int ret = EXIT_SUCCESS; @@ -1364,6 +1495,8 @@ int main(int argc, char **argv) GAConfig *config = g_new0(GAConfig, 1); int listen_fd; + update_vmdump_info(s); + config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; qga_qmp_init_marshal(&ga_commands); @@ -1460,6 +1593,7 @@ end: if (s->main_loop) { g_main_loop_unref(s->main_loop); } + g_free(s->vmdump.vmcoreinfo); g_free(s); return ret; -- 2.12.0.191.gc5d8de91d