-
Notifications
You must be signed in to change notification settings - Fork 2
/
memdump.c
444 lines (402 loc) · 11.6 KB
/
memdump.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/*++
/* NAME
/* memdump 1
/* SUMMARY
/* memory dumper
/* SYNOPSIS
/* .ad
/* .fi
/* \fBmemdump\fR [\fB-kv\fR] [\fB-b \fIbuffer_size\fR]
/* [\fB-d \fIdump_size\fR] [\fB-m \fImap_file\fR] [\fB-p \fIpage_size\fR]
/* DESCRIPTION
/* This program dumps system memory to the standard output stream,
/* skipping over holes in memory maps.
/* By default, the program dumps the contents of physical memory
/* (\fB/dev/mem\fR).
/*
/* Output is in the form of a raw dump; if necessary, use the \fB-m\fR
/* option to capture memory layout information.
/*
/* Output should be sent off-host over the network, to avoid changing
/* all the memory in the file system cache. Use netcat, stunnel, or
/* openssl, depending on your requirements.
/*
/* The size arguments below understand the \fBk\fR (kilo) \fBm\fR (mega)
/* and \fBg\fR (giga) suffixes. Suffixes are case insensitive.
/*
/* Options
/* .IP \fB-k\fR
/* Attempt to dump kernel memory (\fB/dev/kmem\fR) rather than physical
/* memory.
/* .sp
/* Warning: this can lock up the system to the point that you have
/* to use the power switch (for example, Solaris 8 on 64-bit SPARC).
/* .sp
/* Warning: this produces bogus results on Linux 2.2 kernels.
/* .sp
/* Warning: this is very slow on 64-bit machines because the entire
/* memory address range has to be searched.
/* .sp
/* Warning: kernel virtual memory mappings change frequently. Depending
/* on the operating system, mappings smaller than \fIpage_size\fR or
/* \fIbuffer_size\fR may be missed or may be reported incorrectly.
/* .IP "\fB-b \fIbuffer_size\fR (default: 0)"
/* Number of bytes per memory read operation. By default, the program
/* uses the \fIpage_size\fR value.
/* .sp
/* Warning: a too large read buffer size causes memory to be missed on
/* FreeBSD or Solaris.
/* .IP "\fB-d \fIdump-size\fR (default: 0)"
/* Number of memory bytes to dump. By default, the program runs
/* until the memory device reports an end-of-file (Linux), or until
/* it has dumped from \fB/dev/mem\fR as much memory as reported present
/* by the kernel (FreeBSD, Solaris), or until pointer wrap-around happens.
/* .sp
/* Warning: a too large value causes the program to spend a lot of time
/* skipping over non-existent memory on Solaris systems.
/* .sp
/* Warning: a too large value causes the program to copy non-existent
/* data on FreeBSD systems.
/* .IP "\fB-m\fR \fImap_file\fR"
/* Write the memory map to \fImap_file\fR, one entry per line.
/* Specify \fB-m-\fR to write to the standard error stream.
/* Each map entry consists of a region start address and the first
/* address beyond that region. Addresses are separated by space,
/* and are printed as hexadecimal numbers (0xhhhh).
/* .IP "\fB-p \fIpage_size\fR (default: 0)"
/* Use \fIpage_size\fR as the memory page size. By default the program
/* uses the system page size.
/* .sp
/* Warning: a too large page size causes memory to be missed
/* while skipping over holes in memory.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the program more verbose.
/* BUGS
/* On many hardware platforms the firmware (boot PROM, BIOS, etc.)
/* takes away some memory. This memory is not accessible through
/* \fB/dev/mem\fR.
/*
/* This program should produce output in a format that supports
/* structure information such as ELF.
/* LICENSE
/* This software is distributed under the IBM Public License.
/* AUTHOR
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* USA
/*--*/
/* System libraries. */
#include <sys/types.h>
#include <sys/mman.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef SUNOS5
#include <errno.h>
#define _PATH_MEM "/dev/mem"
#define _PATH_KMEM "/dev/kmem"
#define GETPAGESIZE() sysconf(_SC_PAGESIZE)
#define SUPPORTED
#endif
#if defined(FREEBSD2) || defined(FREEBSD3) || defined(FREEBSD4) \
|| defined(FREEBSD5) || defined(FREEBSD6) || defined(FREEBSD7) \
|| defined(OPENBSD2) || defined(OPENBSD3) \
|| defined(BSDI2) || defined(BSDI3) || defined(BSDI4)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <paths.h>
#define GETPAGESIZE getpagesize
#define SUPPORTED
#endif
#ifdef LINUX2
#include <paths.h>
#define GETPAGESIZE getpagesize
#define SUPPORTED
#endif
/*
* Catch-all.
*/
#ifndef SUPPORTED
#error "This operating system version is not supported"
#endif
/* Application-specific. */
#include "convert_size.h"
#include "error.h"
#include "mymalloc.h"
/*
* Default settings.
*/
#define DEF_BUFF_SIZE 0 /* use page size */
#define DEF_PAGE_SIZE 0 /* use page size */
#define DEF_SCAN_SIZE 0 /* all memory */
#define OFFT_TYPE unsigned long
#define OFFT_CAST unsigned long
#define OFFT_FMT "lx"
/* usage - complain, explain, and terminate */
static void usage(const char *why)
{
if (why)
remark("%s", why);
error("usage: %s [options]\n"
" -b read_buffer_size"
" (default %lu, use the system page size)\n"
" -k "
" (dump kernel memory instead of physical memory)\n"
" -m map_file "
" (print memory map)\n"
" -p memory_page_size"
" (default %lu, use the system page size)\n"
" -s memory_dump-size"
" (default %lu, dump all memory)\n"
" -v "
" (verbose mode for debugging)",
progname, (unsigned long) DEF_BUFF_SIZE,
(unsigned long) DEF_PAGE_SIZE,
(unsigned long) DEF_SCAN_SIZE);
}
/* get_memory_size - figure out the memory size if we need to */
static OFFT_TYPE get_memory_size(void)
{
#ifdef SUNOS5
OFFT_TYPE pagesz = sysconf(_SC_PAGESIZE);
OFFT_TYPE pagect = sysconf(_SC_PHYS_PAGES);
return (pagesz * pagect);
#endif
#if defined(FREEBSD2) || defined(FREEBSD3) || defined(FREEBSD4) \
|| defined(FREEBSD5) \
|| defined(OPENBSD2) || defined(OPENBSD3)
int name[] = {CTL_HW, HW_PHYSMEM};
size_t len;
unsigned unsigned_memsize;
unsigned long ulong_memsize;
OFFT_TYPE offt_memsize;
if (sysctl(name, 2, (void *) 0, &len, (void *) 0, 0) < 0)
error("sysctl: %m");
if (len == sizeof(unsigned)) {
if (sysctl(name, 2, &unsigned_memsize, &len, (void *) 0, 0) <0)
error("sysctl: %m");
return (unsigned_memsize);
} else if (len == sizeof(unsigned long)) {
if (sysctl(name, 2, &ulong_memsize, &len, (void *) 0, 0) <0)
error("sysctl: %m");
return (ulong_memsize);
} else if (len == sizeof(OFFT_TYPE)) {
if (sysctl(name, 2, &offt_memsize, &len, (void *) 0, 0) < 0)
error("sysctl: %m");
return (offt_memsize);
} else {
error("unexpected sizeof(hw.physmem): %d", len);
}
#endif
return (0);
}
/* dump_memory - dump memory blocks between holes */
static void dump_memory(int fd, FILE * map, char *buffer, size_t buffer_size,
size_t dump_size, size_t page_size, int flags)
{
OFFT_TYPE start;
OFFT_TYPE where;
OFFT_TYPE count;
size_t todo;
ssize_t read_count;
int in_region = 0;
#define ENTER_REGION(map, flags, start, where, in_region) { \
if (map) \
start = where; \
in_region = 1; \
}
#define LEAVE_REGION(map, flags, start, where, in_region) { \
if (map) \
fprintf(map, "0x%" OFFT_FMT " 0x%" OFFT_FMT "\n", \
(OFFT_CAST) start, \
(OFFT_CAST) where); \
in_region = 0; \
}
for (where = 0, count = 0; dump_size == 0 || count < dump_size; /* increment at end */ ) {
/*
* Some systems don't detect EOF, so don't try to read too much.
*/
todo = (dump_size > 0 && dump_size - count < buffer_size ?
dump_size - count : buffer_size);
#ifdef USE_PREAD
read_count = pread(fd, buffer, todo, where);
#else
read_count = read(fd, buffer, todo);
#endif
if (read_count == 0) {
if (verbose)
remark("Stopped on EOF at 0x%" OFFT_FMT, (OFFT_CAST) where);
break;
}
if (read_count > 0) {
if (in_region == 0)
ENTER_REGION(map, flags, start, where, in_region);
if (write(1, buffer, read_count) != read_count)
error("output write error: %m");
count += read_count;
if (where + read_count < where) {
remark("Stopped on OFFT_TYPE wraparound after 0x%" OFFT_FMT,
(OFFT_CAST) where);
break;
}
where += read_count;
if (verbose > 1)
remark("count = 0x%" OFFT_FMT, (OFFT_CAST) count);
}
if (read_count < 0) {
if (in_region)
LEAVE_REGION(map, flags, start, where, in_region);
if (where + page_size < where) {
remark("Stopped on OFFT_TYPE wraparound after 0x%" OFFT_FMT,
(OFFT_CAST) where);
break;
}
#ifdef USE_PREAD
if (errno != EFAULT) {
if (verbose)
remark("Stopped on read error after 0x%" OFFT_FMT ": %m",
(OFFT_CAST) where);
break;
}
#else
if (lseek(fd, where + page_size, SEEK_SET) < 0) {
if (verbose)
remark("Stopped on lseek error after 0x%" OFFT_FMT,
(OFFT_CAST) where);
break;
}
#endif
where += page_size;
if (verbose > 1)
remark("where = 0x%" OFFT_FMT, (OFFT_CAST) where);
}
/*
* Kluge to prevent pointer wrap-around.
*/
if (where != (OFFT_TYPE) (unsigned long) (char *) (unsigned long) where) {
if (verbose)
remark("Stopped on pointer wraparound at 0x%" OFFT_FMT,
(OFFT_CAST) where);
break;
}
}
if (in_region)
LEAVE_REGION(map, flags, start, where, in_region);
/*
* Sanity check.
*/
if (dump_size > 0 && where < dump_size)
remark("warning: found only 0x%" OFFT_FMT " of 0x%" OFFT_FMT "bytes",
(OFFT_CAST) count, (OFFT_CAST) dump_size);
close(fd);
}
/* main - main program */
int main(int argc, char **argv)
{
size_t page_size = DEF_PAGE_SIZE;
size_t dump_size = DEF_SCAN_SIZE;
char *buffer;
size_t buffer_size = DEF_BUFF_SIZE;
int ch;
int flags = 0;
int fd;
FILE *map = 0;
const char *path = _PATH_MEM;
progname = argv[0];
/*
* Parse JCL.
*/
while ((ch = getopt(argc, argv, "b:km:p:s:v")) > 0) {
switch (ch) {
/*
* Read buffer size.
*/
case 'b':
if ((buffer_size = convert_size(optarg)) == -1)
usage("bad read buffer size");
break;
/*
* Dump kernel memory instead of physical memory.
*/
case 'k':
path = _PATH_KMEM;
break;
/*
* Show memory map.
*/
case 'm':
if (strcmp(optarg, "-") == 0) {
map = stderr;
} else {
if ((map = fopen(optarg, "w")) == 0)
error("create map file %s: %m", optarg);
}
break;
/*
* Page size.
*/
case 'p':
if ((page_size = convert_size(optarg)) == -1)
usage("bad memory size");
break;
/*
* Amount of memory to copy.
*/
case 's':
if ((dump_size = convert_size(optarg)) == -1)
usage("bad memory dump size");
break;
/*
* Verbose mode.
*/
case 'v':
verbose++;
break;
/*
* Error.
*/
default:
usage((char *) 0);
break;
}
}
/*
* Sanity checks.
*/
if (page_size == 0)
page_size = GETPAGESIZE();
if (buffer_size == 0)
buffer_size = page_size;
if (dump_size == 0 && strcmp(path, _PATH_KMEM) != 0)
dump_size = get_memory_size();
/*
* Audit trail.
*/
if (verbose) {
remark("dump size 0x%" OFFT_FMT, (OFFT_CAST) dump_size);
remark("page size 0x%" OFFT_FMT, (OFFT_CAST) page_size);
remark("buffer size 0x%" OFFT_FMT, (OFFT_CAST) buffer_size);
}
/*
* Allocate buffer. This does not need to be a multiple of the page size.
*/
if ((buffer = mymalloc(buffer_size)) == 0)
error("no read buffer memory available");
/*
* Dump memory, skipping holes.
*/
if (optind != argc)
usage("too many arguments");
if ((fd = open(path, O_RDONLY, 0)) < 0)
error("open %s: %m", path);
dump_memory(fd, map, buffer, buffer_size,
dump_size, page_size, flags);
if (map && fclose(map))
error("map file write error: %m");
exit(0);
}