Analyzing segmentation fault without core file - linux

Suppose my binaries are running in a customer site where I cannot enable core dump generation using ulimit -c . How do engineers debug the segmentation faults in such real world scenarios? Is there any other method of debugging or identifying crashes without core dumps generated.

In the past, I had to deal with this kind of restriction on several occasions. A segmentation fault or, more generally, abnormal process termination had to be investigated with the caveat that a core dump was not available.
For Linux, our platform of choice for this walkthrough, a few reasons come to mind:
Core dump generation is disabled altogether (using limits.conf or ulimit)
The target directory (current working directory or a directory in /proc/sys/kernel/core_pattern) does not exist or is inaccessible due to filesystem permissions or SELinux
The target filesystem has insufficient diskspace resulting in a partial dump
For all of those, the net result is the same: there's no (valid) core dump to use for analysis. Fortunately, a workaround exists for post-mortem debugging that has the potential to save the day, but given it's inherent limitations, your mileage may vary from case to case.
Identifying the Faulting Instruction
The following sample contains a classic use-after-free memory error:
#include <iostream>
struct Test
{
const std::string &m_value;
Test(const std::string &value):
m_value(value)
{
}
void print()
{
std::cout << m_value << std::endl;
}
};
int main()
{
std::string *value = new std::string("this is a test");
Test test(*value);
delete value;
test.print();
return 0;
}
After delete value, the std::string reference Test::m_value points to inaccessible memory. Therefore, running it results in a segmentation fault:
$ ./a.out
Segmentation fault
When a process terminates due to an access violation, the Linux kernel creates a log entry accessible via dmesg and, depending on the system's configuration, the syslog (usually /var/log/messages). The example (compiled with -O0) creates the following entry:
$ dmesg | grep segfault
[80440.957955] a.out[7098]: segfault at ffffffffffffffe8 ip 00007f9f2c2b56a3 sp 00007ffc3e75bc48 error 5 in libstdc++.so.6.0.19[7f9f2c220000+e9000]
The corresponding Linux kernel source from arch/x86/mm/fault.c:
printk("%s%s[%d]: segfault at %lx ip %px sp %px error %lx",
loglvl, tsk->comm, task_pid_nr(tsk), address,
(void *)regs->ip, (void *)regs->sp, error_code);
The error (error_code) reveals what the trigger was. It's a CPU-specific bit set (x86). In our case, the value 5 (101 in binary) indicates that the page represented by the faulting address 0xffffffffffffffe8 was mapped but inaccessible due to page protection and a read was attempted.
The log message identifies the module that executed the faulting instruction: libstdc++.so.6.0.1. The sample was compiled without optimization, so the call to std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) was not inlined:
400bef: e8 4c fd ff ff callq 400940 <_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RK
SbIS4_S5_T1_E#plt>
The STL performs the read access. Knowing those basics, how can we identify where the segmentation fault occurred exactly? The log entry features two essential addresses we need for doing so:
ip 00007f9f2c2b56a3 [...] error 5 in
^^^^^^^^^^^^^^^^
libstdc++.so.6.0.19[7f9f2c220000+e9000]
^^^^^^^^^^^^
The first is the instruction pointer (rip) at the time of the access violation, the second is the address the .text section of the library is mapped to. By subtracting the .text base address from rip, we get the relative address of the instruction in the library and can disassemble the implementation using objdump (you can simply search for the offset):
0x7f9f2c2b56a3-0x7f9f2c220000=0x956a3
$ objdump --demangle -d /usr/lib64/libstdc++.so.6
[...]
00000000000956a0 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, s
td::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<ch
ar>, std::allocator<char> > const&)##GLIBCXX_3.4>:
956a0: 48 8b 36 mov (%rsi),%rsi
956a3: 48 8b 56 e8 mov -0x18(%rsi),%rdx
^^^^^
956a7: e9 24 4e fc ff jmpq 5a4d0 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)#plt>
956ac: 0f 1f 40 00 nopl 0x0(%rax)
[...]
Is that the correct instruction? We can consult GDB to confirm our analysis:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b686a3 in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () from /lib64/libstdc++.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-323.el7_9.x86_64 libgcc-4.8.5-44.el7.x86_64 libstdc++-4.8.5-44.el7.x86_64
(gdb) disass
Dump of assembler code for function _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E:
0x00007ffff7b686a0 <+0>: mov (%rsi),%rsi
=> 0x00007ffff7b686a3 <+3>: mov -0x18(%rsi),%rdx
0x00007ffff7b686a7 <+7>: jmpq 0x7ffff7b2d4d0 <_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l#plt>
End of assembler dump.
GDB shows the very same instruction. We can also use a debugging session to verify the read address:
(gdb) print /x $rsi-0x18
$2 = 0xffffffffffffffe8
This value matches the read address in the log entry.
Identifying the Callers
So, despite the absence of a core dump, the kernel output enables us to identify the exact location of the segmentation fault. In many scenarios, though, that is far from being enough. For one thing, we're missing the list of calls that got us to that point - the call stack or stack trace.
Without a dump in the backpack, you have two options to get hold of the callers: you can start your process using catchsegv (a glibc utility) or you can implement your own signal handler.
catchsegv serves as a wrapper, generates the stack trace, and also dumps register values and the memory map:
$ catchsegv ./a.out
*** Segmentation fault
Register dump:
RAX: 0000000002158040 RBX: 0000000002158040 RCX: 0000000002158000
[...]
Backtrace:
/lib64/libstdc++.so.6(_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E+0x3)[0x7f1794fd36a3]
??:?(_ZN4Test5printEv)[0x400bf4]
??:?(main)[0x400b2d]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f179467a555]
??:?(_start)[0x4009e9]
Memory map:
00400000-00401000 r-xp 00000000 08:02 50331747 /home/user/a.out
[...]
7f1794f3e000-7f1795027000 r-xp 00000000 08:02 33600977 /usr/lib64/libstdc++.so.6.0.19
7f1795027000-7f1795227000 ---p 000e9000 08:02 33600977 /usr/lib64/libstdc++.so.6.0.19
7f1795227000-7f179522f000 r--p 000e9000 08:02 33600977 /usr/lib64/libstdc++.so.6.0.19
7f179522f000-7f1795231000 rw-p 000f1000 08:02 33600977 /usr/lib64/libstdc++.so.6.0.19
[...]
How does catchsegv work? It essentially injects a signal handler using LD_PRELOAD and the library libSegFault.so. If your application already happens to install a signal handler for SIGSEGV and you intend to take advantage of libSegFault.so, your signal handler needs to forward the signal to the original handler (as returned by sigaction(SIGSEGV, NULL)).
The second option is to implement the stack trace functionality yourself using a custom signal handler and backtrace(). This allows you to customize the output location and the output itself.
Based on that information, we can essentially do the same we did before (0x7f1794fd36a3-0x7f1794f3e000=0x956a3). This time around, we can go back to the callers to dig deeper. The second frame is represented by the following line:
??:?(_ZN4Test5printEv)[0x400bf4]
0x400bf4 is the address the callee returns to after Test::print(), it's located in the executable. We can visualize the call site as follows:
$ objdump --demangle -d ./a.out
[...]
400bea: bf a0 20 60 00 mov $0x6020a0,%edi
400bef: e8 4c fd ff ff callq 400940 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std:
:char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_trai
ts<char>, std::allocator<char> > const&)#plt>
400bf4: be 70 09 40 00 mov $0x400970,%esi
^^^^^^
400bf9: 48 89 c7 mov %rax,%rdi
400bfc: e8 5f fd ff ff callq 400960 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))#plt>
[...]
Note that the output of objdump matches the address in this instance because we run it against the executable, which has a default base address of 0x400000 on x86_64 - objdump takes that into account. With address space layout randomization (ASLR) enabled (compiled with -fpie, linked with -pie), the base address has to be taken into account as outlined before.
Going back further involves the same steps:
??:?(main)[0x400b2d]
$ objdump --demangle -d ./a.out
[...]
400b1c: e8 af fd ff ff callq 4008d0 <operator delete(void*)#plt>
400b21: 48 8d 45 d0 lea -0x30(%rbp),%rax
400b25: 48 89 c7 mov %rax,%rdi
400b28: e8 a7 00 00 00 callq 400bd4 <Test::print()>
400b2d: b8 00 00 00 00 mov $0x0,%eax
^^^^^^
400b32: eb 2a jmp 400b5e <main+0xb1>
[...]
Until now, we've been manually translating the absolute address to a relative address. Instead, the base address of the module can be passed to objdump via --adjust-vma=<base-address>. That way, the value of rip or a caller's address can be used directly.
Adding Debug Symbols
We've come a long way without a dump. For debugging to be effective, another critical puzzle piece is absent, however: debug symbols. Without them, it can be difficult to map the assembly to the corresponding source code. Compiling the sample with -O3 and without debug information illustrates the problem:
[98161.650474] a.out[13185]: segfault at ffffffffffffffe8 ip 0000000000400a4b sp 00007ffc9e738270 error 5 in a.out[400000+1000]
As a consequence of inlining, the log entry now points to our executable as the trigger. Using objdump gets us to the following:
400a3e: e8 dd fe ff ff callq 400920 <operator delete(void*)#plt>
400a43: 48 8b 33 mov (%rbx),%rsi
400a46: bf a0 20 60 00 mov $0x6020a0,%edi
400a4b: 48 8b 56 e8 mov -0x18(%rsi),%rdx
^^^^^^
400a4f: e8 4c ff ff ff callq 4009a0 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)#plt>
400a54: 48 89 c5 mov %rax,%rbp
400a57: 48 8b 00 mov (%rax),%rax
Part of the stream implementation was inlined, making it harder to identify the associated source code. Without symbols, you have to use export symbols, calls (like operator delete(void*)) and the surrounding instructions (mov $0x6020a0 loads the address of std::cout: 00000000006020a0 <std::cout##GLIBCXX_3.4>) for the purpose of orientation.
With debug symbols (-g), more context is available by calling objdump with --source:
400a43: 48 8b 33 mov (%rbx),%rsi
operator<<(basic_ostream<_CharT, _Traits>& __os,
const basic_string<_CharT, _Traits, _Alloc>& __str)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 586. string inserter not a formatted function
return __ostream_insert(__os, __str.data(), __str.size());
400a46: bf a0 20 60 00 mov $0x6020a0,%edi
400a4b: 48 8b 56 e8 mov -0x18(%rsi),%rdx
^^^^^^
400a4f: e8 4c ff ff ff callq 4009a0 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)#plt>
400a54: 48 89 c5 mov %rax,%rbp
That worked as expected. In the real world, debug symbols are not embedded in the binaries - they are managed in separate debuginfo packages. In those circumstances, objdump ignores debug symbols even if they are installed. To address this limitation, symbols have to be re-added to the affected binary. The following procedure creates detached symbols and re-adds them using eu-unstrip from elfutils to the benefit of objdump:
# compile with debug info
g++ segv.cxx -O3 -g
# create detached debug info
objcopy --only-keep-debug a.out a.out.debug
# remove debug info from executable
strip -g a.out
# re-add debug info to executable
eu-unstrip ./a.out ./a.out.debug -o ./a.out-debuginfo
# objdump with executable containing debug info
objdump --demangle -d ./a.out-debuginfo --source
Using GDB instead of objdump
Thus far, we've been using objdump because it's usually available, even on production systems. Can we just use GDB instead? Yes, by executing gdb with the module of interest. I use 0x0x400a4b as in the previous objdump invocation:
$ gdb ./a.out
[...]
(gdb) disass 0x400a4b
Dump of assembler code for function main():
[...]
0x0000000000400a43 <+67>: mov (%rbx),%rsi
0x0000000000400a46 <+70>: mov $0x6020a0,%edi
0x0000000000400a4b <+75>: mov -0x18(%rsi),%rdx
0x0000000000400a4f <+79>: callq 0x4009a0 <_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l#plt>
0x0000000000400a54 <+84>: mov %rax,%rbp
In contrast to objdump, GDB can deal with external symbol information without a hitch. disass /m corresponds to objdump --source:
(gdb) disass /m 0x400a4b
Dump of assembler code for function main():
[...]
21 Test test(*value);
22 delete value;
0x0000000000400a25 <+37>: test %rbx,%rbx
0x0000000000400a28 <+40>: je 0x400a43 <main()+67>
0x0000000000400a3b <+59>: mov %rbx,%rdi
0x0000000000400a3e <+62>: callq 0x400920 <_ZdlPv#plt>
23 test.print();
24 return 0;
25 }
0x0000000000400a88 <+136>: add $0x18,%rsp
[...]
End of assembler dump.
In case of an optimized binary, GDB might skip instructions in this mode if the source code cannot be mapped unambiguously. Our instruction at 0x400a4b is not listed. objdump never skips instructions and might skip the source context instead - an approach, that I prefer for debugging at this level. This does not mean that GDB is not useful for this task, it's just something to be aware of.
Final Thoughts
Termination reason, registers, memory map, and stack trace. It's all there without even a trace of a core dump. While definitely useful (I fixed quite a few crashes that way), you have to keep in mind that you're still missing valuable information by going that route, most notably the stack and heap as well as per-thread data (thread metadata, registers, stack).
So, whatever the scenario may be, you should seriously consider enabling core dump generation and ensure that dumps can be generated successfully if push comes to shove. Debugging in itself is complex enough, debugging without information you could technically have needlessly increases complexity and turnaround time, and, more importantly, significantly lowers the probability that the root cause can be found and addressed in a timely manner.

Related

Retrieving memory data with non-canonical-address causes SIGSEGV rather than SIGBUS

I can not produce a "Bus error" with the following assembly code. Here the memory address I use is not a legal "canonical-address". So, how can I trigger that error?
I was running this snippet of code under Ubuntu 20.04 LTS with NASM 2.14.02, but it results in a SIGSEGV segmentation fault on the load, not SIGBUS.
global _start
section .text
_start:
mov rax, [qword 0x11223344557788]
mov rax, 60
xor rdi, rdi
syscall
Corresponding X86-64 assembly code after compiling:
Disassembly of section .text:
0000000000401000 <_start>:
401000: 48 a1 88 77 55 44 33 movabs 0x11223344557788,%rax
401007: 22 11 00
40100a: b8 3c 00 00 00 mov $0x3c,%eax
40100f: 48 31 ff xor %rdi,%rdi
401012: 0f 05 syscall
If you review the Instruction Set Architecture manual for the MOV instruction you would find that accessing a non-canonical address yields a #GP(0) General Protection Fault:
Linux maps all #GP exceptions to SIGSEGV signal (Segmentation Fault). However, in Linux there is a way for a non-canonical address to cause a Bus Error and that is by getting the processor to raise an #SS (Stack Segment) exception. Linux maps #SS exceptions to the SIGBUS signal. Setting the stack pointer to a non-canonical address and then performing a stack related operation will produce such an exception.
This code should produce a Bus Error:
global _start
section .text
_start:
mov rsp, 0x8000000000000000 ; Set RSP to a non-canonical address
push rax ; Pushing value on stack should produce BUS ERROR
One other way of producing a Bus Error on Linux is to raise an #AC (Alignment Check) exception. If you write ring 3 (user) code that enables the Alignment Check bit (bit 18) in RFLAGS and do an unaligned memory access you should also receive a SIGBUS signal. This code should produce a Bus Error:
global _start
section .text
_start:
pushf ; Put current RFLAGS on the stack
or dword [rsp], 1<<18 ; Enable bit 18 (Alignment Check) of the
; RFLAGS value saved on stack
popf ; Pop new RFLAGS flags value into the RFLAGS register
mov eax, [rsp + 1] ; Move a DWORD value from unaligned address
; Should produce a BUS ERROR

Get machine code of the proccess by PID without attaching a debugger

I want to get a machine code of the running proccess by his PID for analysing malicious instructions, by using heuristic methods of data analysing.
All I need to know is list of current machine instructions and values of registers (EIP, EAX, EBX...).
I can use gdb for reach this goal gdb output, but is take a several problems:
I don't know how interact with gdb from my application;
malicious code can use some technics of debugger detection like this: http://www.ouah.org/linux-anti-debugging.txt
https://www.youtube.com/watch?v=UTVp4jpJoyc&list=LLw7XNcx80oj63tRYAg7hrsA
for windows;
Getting info from console output makes work of my application slower.
Is are any way to get this information by PID in Linux? Or maybe Windows?
you may have a look to gcore:
$ gcore
usage: gcore [-o filename] pid
so you can dump process core using its pid:
$ gcore 792
warning: Could not load vsyscall page because no executable was specified
0x00007f5f73998410 in ?? ()
Saved corefile core.792
and then open it in gdb:
$ gdb -c core.792
GNU gdb (GDB) Fedora 8.0.1-30.fc26
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
[...]
[New LWP 792]
Missing separate debuginfo for the main executable file
Try: dnf --enablerepo='*debug*' install /usr/lib/debug/.build-id/09/b9d38bb6291b6282de4a2692e45448828d50da
Core was generated by `./a.out'.
#0 0x00007f5f73998410 in ?? ()
(gdb) info registers
rax 0xfffffffffffffe00 -512
rbx 0x0 0
rcx 0x7f5f73998410 140047938061328
rdx 0x1 1
rsi 0x7ffd30683d73 140725415591283
rdi 0x3 3
rbp 0x7ffd30683d90 0x7ffd30683d90
rsp 0x7ffd30683d68 0x7ffd30683d68
r8 0x1d 29
r9 0x0 0
r10 0x3 3
r11 0x246 582
r12 0x4006d0 4196048
r13 0x7ffd30683e70 140725415591536
r14 0x0 0
r15 0x0 0
rip 0x7f5f73998410 0x7f5f73998410
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
or even using the binary image from /proc to get some symbols:
gdb -c core.792 /proc/792/exe
You may know that you can pass scripts to gdb, this can ease not having to interact with it from your binary (man gdb for more details).
if you don't want to use gdb directly you may try using ptrace() directly, but it is for sure more work.
For the anti debugging technics, well... they work and there is no easy way to handle them directly as far as I know, each one may be worked arounded manually, (patching binary, disassembling from unaligned addresses manually by setting then in objdump, etc...)
I'm not an expert of the domain, I hope this will help you a bit.

How to check which symbols on my shared library have non-position independent code (PIC)?

I'm trying to build a .deb package with debuild -i -us -uc -b and in the end I see:
Now running lintian...
warning: the authors of lintian do not recommend running it with root privileges!
W: libluajit-5.1-2: hardening-no-relro usr/lib/powerpc64le-linux-gnu/libluajit-5.1.so.2.1.0
E: libluajit-5.1-2: shlib-with-non-pic-code usr/lib/powerpc64le-linux-gnu/libluajit-5.1.so.2.1.0
W: luajit: hardening-no-relro usr/bin/luajit-2.1.0-alpha
W: luajit: binary-without-manpage usr/bin/luajit-2.1.0-alpha
Finished running lintian.
I have a hunch that I failed to define a "PIC code setup", which must be at the beginning of each external function:
The following code might appear in a PIC code setup sequence to compute
the distance from a function entry point to the TOC base:
addis 2,12,.TOC.-func#ha
addi 2,2,.TOC.-func#l
as specified by the ABI, page 99.
However I couldn't find the symbols which were non-PIC. Or maybe some relevant file that was not compiled with -fPIC?
Info:
system architecture: ppc64le
compiling .so library with: gcc -shared -fPIC
To find which symbols made your elf non-PIC/PIE (Position Independent Code/Executable), use scanelf from pax-utils package (on ubuntu, install it with sudo apt-get install pax-utils):
$ scanelf -qT /usr/local/lib/libluajit-5.1.so.2.1.0 | head -n 3
libluajit-5.1.so.2.1.0: buf_grow [0x7694] in (optimized out: previous lj_BC_MODVN) [0x7600]
libluajit-5.1.so.2.1.0: buf_grow [0x769C] in (optimized out: previous lj_BC_MODVN) [0x7600]
libluajit-5.1.so.2.1.0: buf_grow [0x76A0] in (optimized out: previous lj_BC_MODVN) [0x7600]
$ objdump -Sa /usr/local/lib/libluajit-5.1.so.2.1.0 | grep -A5 \ 7694:
7694: 00 00 80 39 li r12,0
7698: c6 07 8c 79 rldicr r12,r12,32,31
769c: 00 00 8c 65 oris r12,r12,0
76a0: 00 00 8c 61 ori r12,r12,0
76a4: a6 03 89 7d mtctr r12
76a8: 21 04 80 4e bctrl
On my case an absolute address was meant to be load on r12, but that's not possible for a dynamic library, so the linker used 0 for that parameter (I had to use #GOT operator, but that's the particular solution to my case).
On the luajit program, it's possible to define the address on linking time and it looks like this:
1003d0d4: 00 00 80 39 li r12,0
1003d0d8: c6 07 8c 79 rldicr r12,r12,32,31
1003d0dc: 07 10 8c 65 oris r12,r12,4103
1003d0e0: 30 ca 8c 61 ori r12,r12,51760
1003d0e4: a6 03 89 7d mtctr r12
Quite different right?
a much detailed explanation can be found on this wonderful Gentoo wiki page.
The failing lintian check is this:
# Now that we're sure this is really a shared library, report on
# non-PIC problems.
if ($objdump->{$cur_file}->{TEXTREL}) {
tag 'shlib-with-non-pic-code', $cur_file;
}
So you can probably find the offending file by looking for a .o that contains a TEXTREL dynamic section (which is making its way into your final link).
To do this, you can use readelf --dyanamic, in something like the following:
find . -name '*.o' |
while read obj
do
if readelf --dynamic "$obj" | grep -q TEXTREL
then
echo "$obj contains a TEXTREL section"
fi
done

objdump and udis86 produce different output when disassembling /proc/kcore

I need to disassemble /proc/kcore file in Linux and I need to obtain virtual addresses of some special instructions to put kprobes later on it. According to this document /proc/kcore is an image of physical memory, but in this question someone answered that it is kernel's virtual memory (exactly what I am looking for).
When I use objdump tool to disassemble it, it starts with address something like f7c0b000, but udis86 starts with 0x0 (and totally different instruction). When I try to grep some specific instruction, let's say mov 0xf7c1d60c,%edx, I got:
objdump
f7c0b022 mov 0xf7c1d60c,%edx
udis86
290ec02a mov 0xf7c1d60c,%edx
It looks like the offset between udis86 and objdump is always 0xbffff000. Why so strange offset? How can I obtain virtual address of specific instruction? Somewhere I've read, that kernel is statically mapped at virtual address 0xc0000000 + 0x100000. If /proc/kcore is really physical image, is it correct only to add 0x100000 to addresses returned by objdump and I will get virtual address?
objdump understands ELF format files (such as /proc/kcore). It is able to extract the executable sections of the file while ignoring non-executable content (such as .note sections).
You can see the structure of an ELF exectuable using the -h flag, for example:
# objdump -h /proc/kcore
/proc/kcore: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 note0 00001944 0000000000000000 0000000000000000 000002a8 2**0
CONTENTS, READONLY
1 .reg/0 000000d8 0000000000000000 0000000000000000 0000032c 2**2
CONTENTS
2 .reg 000000d8 0000000000000000 0000000000000000 0000032c 2**2
CONTENTS
3 load1 00800000 ffffffffff600000 0000000000000000 7fffff602000 2**12
CONTENTS, ALLOC, LOAD, CODE
(...)
It looks like the udcli tool from udis86 probably starts disassembling things from the beginning of the file, which suggests that your output will probably start with a bunch of irrelevant output and it's up to you to figure out where execution starts.
UPDATE
Here's the verification. We use this answer to extract the first load section from /proc/kcore, like this:
# dd if=/proc/kcore of=mysection bs=1 skip=$[0x7fffff602000] count=$[0x00800000]
And now if we view that with udcli:
# udcli mysection
0000000000000000 48 dec eax
0000000000000001 c7c060000000 mov eax, 0x60
0000000000000007 0f05 syscall
0000000000000009 c3 ret
000000000000000a cc int3
000000000000000b cc int3
We see that it looks almost identical to the output of objdump -d /proc/kcore:
# objdump -d /proc/kcore
/proc/kcore: file format elf64-x86-64
Disassembly of section load1:
ffffffffff600000 <load1>:
ffffffffff600000: 48 c7 c0 60 00 00 00 mov $0x60,%rax
ffffffffff600007: 0f 05 syscall
ffffffffff600009: c3 retq
ffffffffff60000a: cc int3
ffffffffff60000b: cc int3

how to disassemble a system call?

How could I disassemble system call, so that i could get the assembly instructions involved in it
Well, you could do something like this. Say I wanted to get an assembly dump of "dup":
Write this:
#include <stdio.h>
#include <sys/file.h>
int main() {
return dup(0)
}
Compile it:
gcc -o systest -g3 -O0 systest.c
Dump it:
objdump -d systest
Looking in "main" I see:
400478: 55 push %rbp
400479: 48 89 e5 mov %rsp,%rbp
40047c: bf 00 00 00 00 mov $0x0,%edi
400481: b8 00 00 00 00 mov $0x0,%eax
400486: e8 1d ff ff ff callq 4003a8 <dup#plt>
40048b: c9 leaveq
40048c: c3 retq
40048d: 90 nop
40048e: 90 nop
40048f: 90 nop
So looking at "dup#plt" I see:
00000000004003a8 <dup#plt>:
4003a8: ff 25 7a 04 20 00 jmpq *2098298(%rip) # 600828 <_GLOBAL_OFFSET_TABLE_+0x20>
4003ae: 68 01 00 00 00 pushq $0x1
4003b3: e9 d0 ff ff ff jmpq 400388 <_init+0x18>
So it's making a call into a "global offset table", which I would assume has all the syscall vectors. Like the other post said, see the kernel source (or standard library sources?) for details on that.
I don't think you want to do this. System call handling is complex (see http://www.ibm.com/developerworks/linux/library/l-system-calls/). Since you have tagged this question with "linux", you can just download the source from kernel.org (which will be far more understandable and informative than the assembly code).
For understanding linux system call, browse through the code.
Important files are:
/include/linux/syscalls.h (all the supported system calls in linux)
/arch/arm/kernel/entry-common.S (implementation of system call at register level)
/arch/arm/kernel/calls.S (system call numbers)
/arch/arm/include/asm/unistd.h (address of system call)
Note: system call table can be addressed only from system.map only.

Resources