Nós todos sabemos que os drivers de dispositivo são as mãos do sistema operacional que tornam possível para o kernel lidar com hardware. Sabemos também que existem dois tipos de dispositivos – character e block, dependendo da forma como eles lidam com transmissões de dados, mas o que dispositivo “miscellaneous” significa? Para colocar de uma maneira simples – ele significa que ele significa. Por um lado, isso pode ser um driver que lida com hardware simples; por outro, é a forma como o Linux nos permite criar dispositivos virtuais, como uma das maneiras de se comunicar com os módulos do kernel, que é exatamente o que precisamos para capturar chamadas do sistema Linux.
Neste artigo, vamos criar um dispositivo simples de caractere virtual, que será usado por um processo de espaço do usuário para instruir o nosso módulo do kernel se ele deve sequestrar ou restaurar determinada chamada de sistema. Esse dispositivo virtual será controlado com a função ioctl. Para simplificar, eu decidi não adicionar read/write handlers a esse dispositivo, pois não é realmente necessário para o que estamos prestes a fazer. Embora seja um recurso bom de ter.
Diversos dispositivos são representados com struct miscdevice, que é declarado em /include/linux/miscdevice.h como
struct miscdevice
{
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
Bem grande, hein? No entanto, só devemos cuidar dos três primeiros membros da estrutura:
minor representa o menor número do dispositivo. É preferível defini-lo como
MISC_DYNAMIC_MINOR (pelo menos para o nosso módulo) a menos que você precise que algum
número específico seja usado.
name é o nome do nosso dispositivo que deve aparecer no sistema de arquivos /dev
struct file_operations é um conjunto de ponteiros para implementações correspondentes de funções IO
e um ponteiro para o módulo proprietário.
Essa estrutura é muito grande para ser apresentada aqui, mas você pode encontrá-la em /include/linux/fs.h
Então, antes de tudo, adicione outro arquivo de inclusão ao seu código (que já escrevi no artigo anterior), com
#include <linux/miscdevice.h>
Em seguida, devemos adicionar handlers personalizados para funções e variáveis globais em que estejamos interessados, ou seja, open, release, ioctl e a variável in_use.
/* We will set this variable to 1 in our open handler and erset it
back to zero in release handler*/
int in_use = 0;
/* This function will be invoked each time a user process attempts
to open our device. You should keep in mind that the prototype
of this function may change along different kernel versions. */
static int our_open(struct inode *inode, struct file *file)
{
/* We would not like to allow multiple processes to open this device */
if(in_use)
return -EBUSY;
in_use++;
printk("device has been opened\n");
return 0;
}
/* This function, in turn, will be called when a process closes our device */
static int our_release(struct inode *inode, struct file *file)
{
in_use--;
printk("device has been closed\n");
return 0;
}
/* This function will handle ioctl calls performed on our device */
static int our_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int retval = 0;
/* We will fill this function in the Part III of this series */
return retval;
}
Agora é hora de criar struct file_operations e struct miscdevice e preencher os campos relevantes:
static const struct file_operations our_fops =\
{
.owner = THIS_MODULE,
.open = &our_open,
.release = &our_release,
.unlocked_ioctl = (void*)&our_ioctl,
.compat_ioctl = (void*)&our_ioctl
}
Uma pergunta razoável seria: “Por que vamos definir unlocked_ioctl e compat_ioctl com o mesmo valor e onde diabos é o ioctl regular?”. Não há nada especial sobre isso. unlocked_ioctl é usado em plataformas de 64 bits e compat_ioctl em 32 bits ou no modo de compatibilidade, e é totalmente normal apontá-las para o mesmo local, desde que a função handler não atrapalhe os tipos acima. Quanto ao ioctl, ele simplesmente não está lá mais…
static struct miscdevice our_device = \
{
MISC_DYNAMIC_MINOR,
"interceptor",
&our_fops
};
Depois de tudo isso, devemos fazer um pequeno ajuste para a nossa função init_module, inserindo o seguinte código:
int retval = misc_register(&our_device);
Você também deve mudar “return 0;” para “return retval;“. O código acima diz ao sistema para registrar um dispositivo miscellaneous descrito pela estrutura miscdevice com o kernel. No caso, ao campo menor é atribuído MISC_DYNAMIC_MINOR (nosso caso exatamente), e o kernel preenche com números aleatórios (do nosso ponto de vista), caso contrário, o menor número solicitado é usado.
Nossa função cleanup_module deveria ter esta linha adicionada:
misc_deregister(&our_device);
a fim de cancelar o nosso dispositivo e removê-lo a partir do sistema.
Nota importante: funções do kernel retornam 0 no caso de sucesso, ou um código de erro no caso de falha.
Por agora, temos um modulo de trabalho do kernel que registra um dispositivo miscellaneous quando carregado e o cancela quando descarregado. Construa-o agora com o comando make. Carregue-o com insmod e verifique o conteúdo do sistema de arquivo /dev. Você verá que há um novo dispositivo chamado “interceptor” com o número 10 como número principal e que sempre tem sido atribuído como menor número. Você pode descarregá-lo agora.
Se desejar, você pode tentar abrir e fechar o dispositivo /dev/interceptor de um processo do usuário e verificar o log com dmesg | tail. Você irá visualizar as linhas “device has been opened” e “device has been closed”, respectivamente. Você também pode tentar abrir o dispositivo a partir de dois processos de usuário ao mesmo tempo, então você vai ver que apenas um processo pode abri-lo com sucesso.
No próximo artigo, vamos adicionar algum código ao nosso módulo, o que tornaria possível para ele realmente corrigir o sys_call_table e substituir as chamadas originais com wrappers personalizados.
Espero que este artigo tenha sido útil. Vejo vocês na próxima!
?
Texto original disponível em http://syprog.blogspot.com.br/2011/10/hijack-linux-system-calls-part-ii.html







