What's in a core dump?
Earlier this year, I tried completing some of pwn college’s Advent of Pwn 2026. Without giving too much away, Day 2 of the challenges involved producing a core dump and analyzing it to retrieve the flag. Although the core dump analysis for that challenge could be as simple as running strings on it, it got me thinking about what exactly is in a core dump and how to effectively use it.
Note: This article is written in the context of the core dumps on Linux, which use the ELF file format!
How do you get a core dump?
Firstly, core dumps are disabled on many systems. To check if you’ll get a core dump when your program crashes, run ulimit -c. If you see 0, you’ll need to increase your core dump limit. This limit is the maximum size of a core dump the OS is allowed to write upon a program crashing. You can just set this number to unlimited, unless you’re absolutely sure of the sizes of the core dumps you’ll be producing. If your limit is too small, the core dump could be truncated at some earlier point, rendering it largely useless for in-depth analysis (which we’ll get to later). To set the core dump to unlimited, just run ulimit -c unlimited.
1ubuntu@2025~day-02:~$ ulimit -c
20
3ubuntu@2025~day-02:~$ ulimit -c unlimited
4ubuntu@2025~day-02:~$ ulimit -c
5unlimited
6ubuntu@2025~day-02:~$Next, not all program crashes result in a core dump. man 7 signal lists that the default disposition for the following signals is to terminate the process and produce a dump:
1 SIGABRT P1990 Core Abort signal from abort(3)
2 SIGBUS P2001 Core Bus error (bad memory access)
3 SIGCONT P1990 Cont Continue if stopped
4 SIGFPE P1990 Core Floating-point exception
5 SIGILL P1990 Core Illegal Instruction
6 SIGIOT - Core IOT trap. A synonym for SIGABRT
7 SIGQUIT P1990 Core Quit from keyboard
8 SIGSEGV P1990 Core Invalid memory reference
9 SIGSYS P2001 Core Bad system call (SVr4);
10 see also seccomp(2)
11 SIGTRAP P2001 Core Trace/breakpoint trap
12 SIGUNUSED - Core Synonymous with SIGSYS
13 SIGXCPU P2001 Core CPU time limit exceeded (4.2BSD);
14 see setrlimit(2)
15 SIGXFSZ P2001 Core File size limit exceeded (4.2BSD);
16 see setrlimit(2)Basically, the OS decided that it’s worthwhile to have a core dump when these signals are sent to a process. This could be because something is corrupted, you accessed memory you shouldn’t have, etc.. If you want to obtain a core dump on a signal that’s not one of the ones specified above, you can set a signal handler to call one of the core dump-causing signals.
1void sigint_handler (int signal)
2{
3 raise(SIGQUIT);
4}You can also use gcore to get a dump of a running process. See man gcore for more information.
1ubuntu@2025~day-02:~$ cat infinite.c
2#include <unistd.h>
3int main(){
4
5 pause();
6
7
8 return 0;
9
10}
11ubuntu@2025~day-02:~$ ./infinite &
12[1] 188
13ubuntu@2025~day-02:~$ ps -p 188
14 PID TTY TIME CMD
15 188 pts/0 00:00:00 infinite
16ubuntu@2025~day-02:~$ #ignore the following warnings, I am running in a sandboxed enviornment
17ubuntu@2025~day-02:~$ gcore 188
18warning: opening /proc/PID/mem file for lwp 188.188 failed: Read-only file system (30)
19warning: opening /proc/self/mem file failed: Read-only file system (30)
20[Thread debugging using libthread_db enabled]
21Using host libthread_db library "/nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libthread_db.so.1".
220x00007f7b396312f4 in pause () from /nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libc.so.6
23Saved corefile core.188
24[Inferior 1 (process 188) detached]
25ubuntu@2025~day-02:~$ ls
26core.188 infinite infinite.c
27ubuntu@2025~day-02:~$ #don't forget to kill your process!
28ubuntu@2025~day-02:~$ fg
29./infinite
30^C
31ubuntu@2025~day-02:~$What is a core dump?
At a glance, a core dump is a snapshot of a process’s memory at the moment it crashed. There’s data about what the stack and heap looked like and what the CPU’s registers contained at the moment it crashed.
The part of a core dump that’s interesting to me is how all of this “snapshot” information is stored: in an ELF file format. ELF, which stands for Executable and Linkable Format, is the file format for binaries on Linux. It’s a super interesting but confusing way of storing symbols, .text (program code), global variables, and other file information. The structure of a core dump and a regular ELF binary are similar, except the core dump will contain the state of a running process, including memory contents and registers. If you’d like to learn more about this file structure, these slides from CMU and this diagram from my favorite systems class at Columbia will be helpful!
What can you do with a core dump?
Since a core dump file is just an ELF binary, you can analyze it like you would any other ELF binary, like when reverse engineering. One of the first steps to analyzing a binary is to run strings, and you can do the same here.
1ubuntu@2025~day-02:~$ strings core.188
2Ps9{
3ps9{
40x9{
5...
6./infinite
7SHELL=/run/dojo/bin/bash
8HOSTNAME=2025~day-02
9PWD=/home/hacker
10...Similarly, you can run the core dump through GDB to see how exactly the program crashed.
1ubuntu@2025~day-02:~$ gdb infinite core.188
2GNU gdb (GDB) 16.3
3Copyright (C) 2024 Free Software Foundation, Inc.
4License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
5This is free software: you are free to change and redistribute it.
6There is NO WARRANTY, to the extent permitted by law.
7Type "show copying" and "show warranty" for details.
8This GDB was configured as "x86_64-unknown-linux-gnu".
9Type "show configuration" for configuration details.
10For bug reporting instructions, please see:
11<https://www.gnu.org/software/gdb/bugs/>.
12Find the GDB manual and other documentation resources online at:
13 <http://www.gnu.org/software/gdb/documentation/>.
14
15For help, type "help".
16Type "apropos word" to search for commands related to "word"...
17Reading symbols from infinite...
18(No debugging symbols found in infinite)
19[New LWP 188]
20[Thread debugging using libthread_db enabled]
21Using host libthread_db library "/nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libthread_db.so.1".
22Core was generated by `./infinite'.
23#0 0x00007f7b396312f4 in pause () from /nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libc.so.6
24(gdb) info registers
25rax 0xfffffffffffffdfe -514
26rbx 0x7fff9b9df2b8 140735804207800
27rcx 0x7f7b396312f4 140167220499188
28rdx 0x7fff9b9df2c8 140735804207816
29rsi 0x7fff9b9df2b8 140735804207800
30rdi 0x1 1
31rbp 0x7fff9b9df190 0x7fff9b9df190
32rsp 0x7fff9b9df188 0x7fff9b9df188
33r8 0x0 0
34r9 0x7f7b39750360 140167221674848
35r10 0x0 0
36r11 0x202 514
37r12 0x1 1
38r13 0x0 0
39r14 0x7f7b39783000 140167221882880
40r15 0x55f38f216db8 94504566746552
41rip 0x7f7b396312f4 0x7f7b396312f4 <pause+20>
42eflags 0x202 [ IF ]
43cs 0x33 51
44ss 0x2b 43
45ds 0x0 0
46es 0x0 0
47fs 0x0 0
48gs 0x0 0
49fs_base 0x7f7b39538740 140167219480384
50gs_base 0x0 0
51(gdb) quit
52ubuntu@2025~day-02:~$Demo 1: Obvious crash
The below shell session shows how a program can be inspected to reveal exactly why it crashed.
1ubuntu@2025~day-02:~$ cat math.c
2#include <stdio.h>
3
4
5int main(){
6
7 int array[5] = {4, 3, 2, 1, 0};
8
9 for (int i = 0; i < 5; i++){
10 int x = array[i];
11 int y = 12 / x;
12 // I hope we wont divide by 0...
13 printf("12 / %d = %d\n", x, y);
14 }
15
16 return 0;
17}
18
19ubuntu@2025~day-02:~$ # compile with symbols using -g
20ubuntu@2025~day-02:~$ gcc -g -Wall math.c -o math
21ubuntu@2025~day-02:~$ ./math
2212 / 4 = 3
2312 / 3 = 4
2412 / 2 = 6
2512 / 1 = 12
26Floating point exception (core dumped) ./math
27ubuntu@2025~day-02:~$ # our program crashed due to a floating point exception, meanig there was an arithmetic error
28ubuntu@2025~day-02:~$ ls
29coal math math.c
30ubuntu@2025~day-02:~$ # this environment configures a core dump to be named "coal" and placed in the current directory
31ubuntu@2025~day-02:~$ # let's inspect the exact cause of the arithmetic error
32ubuntu@2025~day-02:~$ gdb ./math coal
33GNU gdb (GDB) 16.3
34Copyright (C) 2024 Free Software Foundation, Inc.
35License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
36This is free software: you are free to change and redistribute it.
37There is NO WARRANTY, to the extent permitted by law.
38Type "show copying" and "show warranty" for details.
39This GDB was configured as "x86_64-unknown-linux-gnu".
40Type "show configuration" for configuration details.
41For bug reporting instructions, please see:
42<https://www.gnu.org/software/gdb/bugs/>.
43Find the GDB manual and other documentation resources online at:
44 <http://www.gnu.org/software/gdb/documentation/>.
45
46For help, type "help".
47Type "apropos word" to search for commands related to "word"...
48Reading symbols from ./math...
49[New LWP 210]
50[Thread debugging using libthread_db enabled]
51Using host libthread_db library "/nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libthread_db.so.1".
52Core was generated by `./math'.
53Program terminated with signal SIGFPE, Arithmetic exception.
54#0 0x000056290c3f017f in main () at math.c:10
5510 int y = 12 / x;
56(gdb) list
575
586 int array[5] = {4, 3, 2, 1, 0};
597
608 for (int i = 0; i < 5; i++){
619 int x = array[i];
6210 int y = 12 / x;
6311 // I hope we wont divide by 0...
6412 printf("12 / %d = %d\n", x, y);
6513 }
6614
67(gdb) info locals
68x = 0
69y = 12
70i = 4
71array = {4, 3, 2, 1, 0}
72(gdb) quit
73ubuntu@2025~day-02:~$ # gdb again tells us we have a floating point exception
74ubuntu@2025~day-02:~$ # from the gdb session, we see that x = 0, so we did 12 / 0
75ubuntu@2025~day-02:~$ # we divided by 0, which was the cause of the arithmetic errorDemo 2: Subtle crash
Now let’s look at a more complex example where the cause of the crash is more subtle and requires further analysis.
1ubuntu@2025~day-02:~$ cat very_secure_code.c
2#include <stdio.h>
3#include <string.h>
4
5
6/* the type of myfunction_t is
7 * a pointer to a function that
8 * takes in nothing and returns nothing
9 */
10typedef void (*myfunction_t)(void);
11
12void normal_function(void) {
13 printf("normal callback ran\n");
14}
15
16void process_name(const char *input) {
17 char name[8];
18 myfunction_t func = normal_function;
19
20 //cool people use strcpy()
21 strcpy(name, input);
22
23 printf("Hello, %s\n", name);
24 func();
25}
26
27int main(void) {
28 process_name("gggggggggggggggg");
29 return 0;
30}
31ubuntu@2025~day-02:~$ # pause here and test your pwn skills by walking through the code
32ubuntu@2025~day-02:~$ gcc -g -Wall very_secure_code.c -o very_secure_code
33ubuntu@2025~day-02:~$ ./very_secure_code
34Hello, gggggggggggggggg
35Segmentation fault (core dumped) ./very_secure_code
36ubuntu@2025~day-02:~$ ls
37coal very_secure_code very_secure_code.c
38ubuntu@2025~day-02:~$ # this environment configures a core dump to be named "coal" and placed in the current directory
39ubuntu@2025~day-02:~$ gdb ./very_secure_code coal
40GNU gdb (GDB) 16.3
41Copyright (C) 2024 Free Software Foundation, Inc.
42License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
43This is free software: you are free to change and redistribute it.
44There is NO WARRANTY, to the extent permitted by law.
45Type "show copying" and "show warranty" for details.
46This GDB was configured as "x86_64-unknown-linux-gnu".
47Type "show configuration" for configuration details.
48For bug reporting instructions, please see:
49<https://www.gnu.org/software/gdb/bugs/>.
50Find the GDB manual and other documentation resources online at:
51 <http://www.gnu.org/software/gdb/documentation/>.
52
53For help, type "help".
54Type "apropos word" to search for commands related to "word"...
55Reading symbols from ./very_secure_code...
56
57warning: core file may not match specified executable file.
58[New LWP 294]
59[Thread debugging using libthread_db enabled]
60Using host libthread_db library "/nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libthread_db.so.1".
61Core was generated by `./very_secure_code'.
62Program terminated with signal SIGSEGV, Segmentation fault.
63#0 0x000055de6f3511b8 in process_name (input=0x55de6f352023 'g' <repeats 16 times>) at very_secure_code.c:23
6423 func();
65(gdb) bt
66#0 0x000055de6f3511b8 in process_name (input=0x55de6f352023 'g' <repeats 16 times>) at very_secure_code.c:23
67#1 0x000055de6f3511d0 in main () at very_secure_code.c:27
68(gdb) info locals
69name = "gggggggg"
70func = 0x6767676767676767
71(gdb) quit
72ubuntu@2025~day-02:~$ # gdb showed us that the address of "func" was odd, all 67's...
73ubuntu@2025~day-02:~$ # let's run the program through gdb as well to see if the function address is also 67 there
74ubuntu@2025~day-02:~$ gdb ./very_secure_code
75GNU gdb (GDB) 16.3
76Copyright (C) 2024 Free Software Foundation, Inc.
77License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
78This is free software: you are free to change and redistribute it.
79There is NO WARRANTY, to the extent permitted by law.
80Type "show copying" and "show warranty" for details.
81This GDB was configured as "x86_64-unknown-linux-gnu".
82Type "show configuration" for configuration details.
83For bug reporting instructions, please see:
84<https://www.gnu.org/software/gdb/bugs/>.
85Find the GDB manual and other documentation resources online at:
86 <http://www.gnu.org/software/gdb/documentation/>.
87
88For help, type "help".
89Type "apropos word" to search for commands related to "word"...
90Reading symbols from ./very_secure_code...
91(gdb) break process_name
92Breakpoint 1 at 0x117b: file very_secure_code.c, line 17.
93(gdb) next
94The program is not being run.
95(gdb) run
96Starting program: /home/hacker/very_secure_code
97warning: opening /proc/self/mem file failed: Read-only file system (30)
98warning: opening /proc/PID/mem file for lwp 320.320 failed: Read-only file system (30)
99[Thread debugging using libthread_db enabled]
100Using host libthread_db library "/nix/store/wqfs0wh0wp6vdcbbck3wzk5v15qy17m7-glibc-2.40-66/lib/libthread_db.so.1".
101
102Breakpoint 1, process_name (input=0x555555556023 'g' <repeats 16 times>) at very_secure_code.c:17
10317 myfunction_t func = normal_function;
104(gdb) info locals
105name = "\000\000\000\000\000\000\000"
106func = 0x0
107(gdb) next
10820 strcpy(name, input);
109(gdb) info locals
110name = "\000\000\000\000\000\000\000"
111func = 0x555555555159 <normal_function>
112(gdb) next
11322 printf("Hello, %s\n", name);
114(gdb) info locals
115name = "gggggggg"
116func = 0x6767676767676767
117(gdb) quit
118A debugging session is active.
119
120 Inferior 1 [process 320] will be killed.
121
122Quit anyway? (y or n) y
123ubuntu@2025~day-02:~$ # gdb shows us that the address of func changes after the strcpy
124ubuntu@2025~day-02:~$ # we realize that strcpy() is copying too much data and overwriting the function pointer address
125ubuntu@2025~day-02:~$ # so, when func() is called, it's a bad address and causes a seg fault!Takeaways
I hope this article has given you a deeper understanding of core dumps! Many C programmers will come across core dumps but not know of their contents or how to effectively use them for debugging. With these fundamentals, you should be well-equipped to use core dumps to investigate program crashes, diagnose bugs, or uncover valuable information in a CTF challenge.