2005-03-31 11:54:58

by Piotr Muszynski

[permalink] [raw]
Subject: module for controlling kprobes with /proc

/*
* kprobemod.c -- Control Kprobes by /proc

* Copyright (C) 2005 Piotr Muszynski, Tohoku Ricoh Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* +-------------------+
* | Debugged module |
* | - - - - - - - - - |
* | |
* | |
* | x --> breakpoint | --+
* | | |
* | | |
* +-->| display_my_data() | |
* | +-------------------+ |
* | |
* | |
* | +-------------------+ |
* | | kprobe module | |
* | | - - - - - - - - - | |
* | | | |
* | | | |
* OR--| kprobe_callback() |<--+
* | | |
* | | - - - - - - - - - |
* | | breakpoint table |
* | | - - - - - - - - - |
* | | /proc interface |
* | +-------------------+
* |
* |
* | +-------------------+
* | | other part of |
* | | kernel |
* | | - - - - - - - - - |
* +-->| do_whatever() |
* +-------------------+
*
*
* kprobemod is commanded from user space by writing
* to /proc file
*
* command format:
* 1 character insert:1, delete:0
* 1 character SEPARATOR
* 10 characters 0x12345678 action (handler function address)
* 1 character SEPARATOR
* 10 characters 0x12345678 where (breakpoint address)
* 1 character SEPARATOR
* 1 character post:1, pre:0
*
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>

#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include </usr/src/linux/include/config/modversions.h>
#endif

#include <linux/kallsyms.h>
#include <linux/kprobes.h>

static struct proc_dir_entry *proc_kprobe_file;

static void insert_probe(
unsigned long action,
unsigned long where,
int when
);
static void remove_probe (unsigned long where);
static void remove_all_probes (void);
static int kprobe_init (void);
static void kprobe_exit (void);
int proc_read (
char *page, char **startp, off_t offset,
int buffer_length, int *eof, void *zero
);
int proc_write (
struct file *file, const char *buffer,
unsigned long count, void *data
);
void call_handle(struct kprobe *kp);
int kp_dummy_pre(struct kprobe *, struct pt_regs *);
int kp_pre(struct kprobe *kp, struct pt_regs *regs);
void kp_dummy_post(struct kprobe *, struct pt_regs *, unsigned long);
void kp_post(
struct kprobe *kp, struct pt_regs *ptregs,
unsigned long flags
);
int kp_fault (struct kprobe *kp, struct pt_regs *regs, int trapnr);

//command string:
// [0] insert:1, delete:0
// [1] SEPARATOR
// [2-11] 0x12345678 action
// [12] SEPARATOR
// [13-22] 0x12345678 where
// [23] SEPARATOR
// [24] post:1, pre:0
struct kprobe_command {
void (*action) (void); // address of handler function to insert
unsigned long where; // address to place the probe at
int when; // 0:pre,1:post
struct kprobe kp;
};

#define MAXPOINTS 20
static struct kprobe_command kpcmd[MAXPOINTS];

//
// Register a probe given address, action handler, pre/post parameter
//
static void insert_probe(
unsigned long action,
unsigned long where,
int when)
{
int loop;
for (loop = 0; loop < MAXPOINTS; loop++)
if ((kpcmd[loop].where == 0) || (kpcmd[loop].where == where))
break;
if (loop == MAXPOINTS) {
printk(KERN_ALERT "kprobe: table full\n");
return;
}
// refuse multiple probes at one address
if (kpcmd[loop].where == where) {
printk(KERN_ALERT "kprobe: address taken on NULL\n");
return;
}
struct kprobe_command *pkpc = &kpcmd[loop];
pkpc->action = (void (*) (void))action;
pkpc->where = where;
pkpc->when = when;
pkpc->kp.fault_handler = kp_fault;
pkpc->kp.addr = (kprobe_opcode_t *)where;
if(0 == when) {
pkpc->kp.pre_handler = kp_pre;
pkpc->kp.post_handler = kp_dummy_post;
} else {
pkpc->kp.post_handler = kp_post;
pkpc->kp.pre_handler = kp_dummy_pre;
}
int result = register_kprobe(&pkpc->kp);
printk(KERN_ALERT "kprobe: register_kprobe() returned %d\n", result);
printk(KERN_ALERT "kprobe: inserted # %04d\n", loop);
}

//
// Remove single probe
//
static void remove_probe (unsigned long where)
{
if (where == 0) {
printk(KERN_ALERT "kprobe: NULL address\n");
return;
}
int loop;
// find probe for given address
// assume one probe per address
for (loop = 0; loop < MAXPOINTS; loop++)
if (kpcmd[loop].where == where)
break;
if (loop == MAXPOINTS) {
printk(KERN_ALERT "kprobe: entry not found\n");
return;
}
struct kprobe_command *pkpc = &kpcmd[loop];

unregister_kprobe(&pkpc->kp);
memset(pkpc, 0, sizeof(struct kprobe_command));
printk(KERN_ALERT "kprobe: removed # %04d\n", loop);
}

//
// Remove all probes
//
static void remove_all_probes (void)
{
int loop;
for (loop = 0; loop < MAXPOINTS; loop++) {
if (kpcmd[loop].where != 0) {
struct kprobe_command *pkpc = &kpcmd[loop];
unregister_kprobe(&pkpc->kp);
memset(pkpc, 0, sizeof(struct kprobe_command));
printk(KERN_ALERT "kprobe: removed # %04d\n", loop);
}
}
}

static int kprobe_init (void)
{
printk(KERN_ALERT "kprobe: insmod()\n");

// access us by writing commands to /proc
proc_kprobe_file = create_proc_entry(
"kprobe", S_IFREG | S_IRUGO | S_IWUSR, &proc_root
);
if (NULL == proc_kprobe_file)
return 1;
proc_kprobe_file->read_proc = proc_read;
proc_kprobe_file->write_proc = proc_write;
proc_kprobe_file->owner = THIS_MODULE;
proc_kprobe_file->mode |= S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH;
proc_kprobe_file->uid = 0;
proc_kprobe_file->gid = 100;

memset(kpcmd, 0, sizeof(kpcmd));

/// kp.addr = (kprobe_opcode_t *)kallsyms_lookup_name("do_fork");

return 0;
}

static void kprobe_exit (void)
{
remove_all_probes();
remove_proc_entry("kprobe", proc_kprobe_file);
printk(KERN_ALERT "kprobe: exit()\n");
}

//
// Show registered probes
//
int proc_read (
char *page,
char **startp,
off_t offset,
int buffer_length,
int *eof,
void *zero)
{
int len = 0;

if(offset > 0)
return 0;
int loop;
for (loop = 0; loop < MAXPOINTS; loop++) {
struct kprobe_command *pkpc = &kpcmd[loop];
if (pkpc->where != 0) {
len += sprintf(
page + len, "%04d: %s->0x%08lx at 0x%08lx\n",
loop,
(pkpc->when) ? "POST":"PRE ",
(unsigned long)pkpc->action,
pkpc->where
);
}
}

return len;
}


//
// Read user's commands
//
int proc_write (
struct file *file,
const char *buffer,
unsigned long count,
void *data)
{
char command[30];
if (count < 28) {
copy_from_user(command, buffer, count);
command[count] = 0;
} else {
printk(KERN_ALERT "kprobe count: 0x%08lx\n", count);
goto error;
}

printk(KERN_ALERT "kprobe command: %s\n", command);

if ((':' == command[1]) && (':' == command[12]) && (':' == command[23]))
{
printk(KERN_ALERT "kprobe command %s OK\n", command);
int when = 0;
unsigned long where = 0, action = 0;
if (command[24] == '1')
when = 1;
char *tmp;
action = simple_strtoul(&command[2], &tmp, 16);
if (tmp - &command[2] < 10)
goto error;
where = simple_strtoul(&command[13], &tmp, 16);
if (tmp - &command[13] < 10)
goto error;
printk(KERN_ALERT "kprobe: good command\n");
printk(KERN_ALERT "kprobe: %d when\n", when);
printk(KERN_ALERT "kprobe: 0x%08lx where\n", where);
printk(KERN_ALERT "kprobe: 0x%08lx action\n", action);

if (command[0] == '1')
insert_probe(action, where, when);
else
remove_probe(where);

return count;
}

error: printk(KERN_ALERT "kprobe: bad command\n");
return -1;
}

//
// Magic occurs here
// We call the other module's debugging function by pointer
// Actually, it can be anything, so be careful
//
void call_handle(struct kprobe *kp)
{
printk(KERN_ALERT "kprobe call_handle()\n");
int loop;
// find the handler
for (loop = 0; loop < MAXPOINTS; loop++) {
struct kprobe_command *pkpc = &kpcmd[loop];
if (kp->addr == (void *)pkpc->where) {
// and call it
(*pkpc->action)();
}
}
}

///
/// Collection of callbacks
/// These are called by kernel when it stumbles upon kprobed address
/// All data we need is in struct kprobe
///
int kp_dummy_pre(struct kprobe *kp, struct pt_regs *regs)
{
return 0;
}

int kp_pre(struct kprobe *kp, struct pt_regs *regs)
{
call_handle(kp);
return 0;
}

void kp_dummy_post(struct kprobe *kp, struct pt_regs *ptregs, unsigned long flags)
{
return;
}

void kp_post(struct kprobe *kp, struct pt_regs *ptregs, unsigned long flags)
{
call_handle(kp);
return;
}

int kp_fault (struct kprobe *kp, struct pt_regs *regs, int trapnr)
{
printk(KERN_ALERT "kp_fault()\n");
return 0;
}

module_init(kprobe_init);
module_exit(kprobe_exit);

MODULE_LICENSE("Dual BSD/GPL");




Attachments:
kprobemod.c (9.25 kB)

2005-04-04 08:34:22

by S. P. Prasanna

[permalink] [raw]
Subject: Re: module for controlling kprobes with /proc

Hi Piotr,

Good way to make kprobes useful, but I have some comments.

>I have programmed a universal module to register/remove kprobes handlers
>by interacting with /proc with simple commands.
>

why /proc ?

You can use a combination of SysRq key to enter a kprobe command line prompt.
Initially you can define a dummy breakpoint for command line prompt and accept
commands from thereon.
Later display the list of features add/remove/display breakpoint, backtrace etc.
Also once you hit a breakpoint you give a command line prompt and user can backtrace/ dump some global memory, dump registers etc.

Let me know if you need more information.

Thanks
Prasanna

--

Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<[email protected]>

2005-04-04 09:42:10

by Piotr Muszynski

[permalink] [raw]
Subject: Re: module for controlling kprobes with /proc

Joel Becker wrote:
>> I have programmed a universal module to register/remove kprobes handlers
>> by interacting with /proc with simple commands.
>
>
> Looking at your code, I'm thinking you could really use
>configfs. With configfs, kernelspace objects are created and controlled
>via regular filesystem operations. Instead of echoing a cryptic line to
>your proc file, a user could instead create objects in a directory:
>
>To insert a handler:
> cd /config/kprobes
> mkdir myhandler
> cd myhandler
> echo 0x12345678 > handler_address
> echo 0x87654321 > breakpoint_address
> echo post > when
>
>To remove one:
> cd /config/kprobes
> rmdir myhandler
>
> If this interests you at all, see
>http://oss.oracle.com/projects/ocfs2/src/trunk/fs/configfs/,
>specifically configfs.txt and configfs_example.c. I'm going to be
>submitting this for mainline inclusion soon.

Joel, thank you very much - this looks like a Good Idea. I am going
to try configfs as soon as my schedule permits.
(Will be grateful for any directions, as I'm new in kernelspace :-))

Piotr
PS Hope you don't mind me quoting your message here.

2005-04-04 09:55:46

by Piotr Muszynski

[permalink] [raw]
Subject: Re: module for controlling kprobes with /proc

Hello Prasanna,

On Apr 4, 2005 5:35 PM, Prasanna S Panchamukhi <[email protected]> wrote:
> why /proc ?

Thank you for your remarks. I originally thought of writing a simple
CLI app to sit over /proc , do basic sanity checks and be simple to
use.

> You can use a combination of SysRq key to enter a kprobe command line prompt.
> Initially you can define a dummy breakpoint for command line prompt and accept
> commands from thereon.
> Later display the list of features add/remove/display breakpoint, backtrace etc.
> Also once you hit a breakpoint you give a command line prompt and user can backtrace/ dump some global memory, dump registers etc.
>
> Let me know if you need more information.

Yes please, I would be grateful.
TIA

Piotr