[Kernel] Kprobe

Brian Pan
3 min readNov 3, 2020

--

Usage of Kprobe

KProbes is a debugging mechanism for the Linux kernel which can also be used for monitoring events inside a production system.

Fetch layout

FETCHARGS   : Arguments. Each probe can have up to 128 args.%REG      : Fetch register REG@ADDR     : Fetch memory at ADDR (ADDR should be in kernel)@SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol)$stackN   : Fetch Nth entry of stack (N >= 0)$stack    : Fetch stack address.$retval   : Fetch return value.(*)$comm     : Fetch current task comm.+|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) -> access ptr type varNAME=FETCHARG : Set NAME as the argument name of FETCHARG.FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types(x8/x16/x32/x64), "string" and bitfield are supported.

Enable an event of kprobe

sys_connect as the example

# pwd -> /sys/kernel/debug/tracing# sys_connect(int, struct sockaddr *, int)# how to find params store in which register -> check the macro of the linux source# PT_REGS_PARM1_CORE, PT_REGS_PARM2_CORE, PT_REGS_PARM3_CORE# second param storing in %si or %edx depends on the arch# The string array type is a bit different from other types. For other base types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same as +0(%di):x32.) But string[1] is not equal to string. The string type itself represents “char array”, but string array type represents “char * array”

sockaddr <-> sockaddr_in

# first 2 bytes are type# bytes 0-1 of sockaddr.sa_data are sockaddr_in.sin_port# bytes 2-5 are sockaddr_in.sin_addr# bytes 6-13 are sockaddr_in.sin_zero

Definition of the structs in kernel

struct sockaddr {  sa_family_t sa_family;  char        sa_data[14];};struct sockaddr_in {  short            sin_family;   // e.g. AF_INET 2  unsigned short   sin_port;     // e.g. htons(3490)  struct in_addr   sin_addr;     // see struct in_addr, below  char             sin_zero[8];  // zero this if you want to};struct in_addr {
uint32_t s_addr; /* address in network byte order */
};

Define Kprobe event


# use byte memory offset and type definition to define kprobe event
# type is u16 which is short in the offset 0 of %si register (second param)
# port is in offset 2 of %si register with unsigned short type
# addr is in offset 4 which is 4 bytes long
sudo echo "p:mysocket_event sys_connect type=+0(%si):u16 port=+2(%si):u16 addr=+4(%si):u32" >> kprobe_events

Enable kprobe event mysocket_event

echo "1" > events/kprobes/mysocket_event/enable

Cleanup the event

# cleanup# echo "0" > events/kprobes/mysocket_event/enable && echo > kprobe_eventsecho "0" > events/kprobes/mysocket_event/enableecho > /sys/kernel/debug/tracing/kprobe_events

Ways to track events

cat /sys/kernel/debug/tracing/trace# read from trace_pipe
cat trace_pipe

Real example data

# netcat -zv 127.0.0.1 25#  netcat-1048  [001] .... 1272634.456626: mysocket_event: (SyS_connect+0x0/0x10) type=2 port=6400 addr=16777343# port=6400 is network byte order
# do the ntohs transformation -> 25
# socket.ntohl(16777343) # long net byte order to host byte order
# 2130706433
# bit operation to convert host byte order to IpV4# 2130706433 % 256 -> 1
# (2130706433 >> 8) % 256 -> 0
# (2130706433 >> 16) % 256 -> 0
# (2130706433 >> 24) % 256 -> 127
# 127.0.0.1

Transformation of network byte order and host byte order

# using python as the example
import socket
socket.ntohs(6400) # 25socket.nthhl(16777343) # 2130706433socket.inet_aton("127.0.0.1") # b'\x7f\x00\x00\x01'int.from_bytes(b'\x7f\x00\x00\x01', "little") #16777343

References

--

--