unix4fun

Aller au contenu | Aller au menu | Aller à la recherche

mercredi 3 avril 2013

CUDA pour piger les bases...

Je me prends la tete depuis qqes jours/semaines de temps en temps sur la doc CUDA,
tout n'est pas très clair, mais ça l'est devenu un peu plus grâce a ce lien :

http://www.caam.rice.edu/~timwar/RMMC/CUDA.html

Enjoy!

samedi 15 septembre 2012

Et voici MoisiFS !

Salut les gens !

Oui oui, vous l'aurez deviné, je présente (façon de parler) ici un truc que je viens de coder : un filesystem FUSE au-dessus des fichiers au format ELF.

Bon alors là, tout de suite, logiquement vous me dites "euh ok c'est bien gentil ton truc mais ça sert à quoi ?", ce à quoi je réponds "rien !". Plus sérieusement, ça sert à se faire un peu la main sur FUSE (donc vous pouvez regarder le code pour vous faire une idée, j'ose espérer qu'il n'est pas trop cracra). Ça peut également servir à des fins pédagogiques : voir la structure d'un fichier ELF, tout ça...

Bon, le but avoué, c'est qu'à terme, on puisse modifier un binaire, ou carrément un processus en cours via le point de montage. Par exemple en faisant un truc du genre :


echo 0x424242 > /mnt/elf/entrypoint
sed -i "s:generic_func:myhook_func:g" /mnt/elf/symbols

Ou autres trucs du genre ! Amusant non ? Non ? Bon.. ok... j'retourne coder d'autres crottes en silence...

Sinon pour l'instant on peut juste faire 2-3 trucs en read-only:

  • monter un binaire sur disque ou un process en cours d'exécution (expérimental, tout ça...)

$ elfs /usr/bin/screen /mnt/elf
ou
$ sudo elfs -p $(pidof screen) /mnt/elf

  • lister les lib (répertoire /mnt/elf/libs/)

$ ls -l /mnt/elf/libs
total 0
-rwxrwxrwx 0 root root  9 1970-01-01 01:00 libc.so.6
-rwxrwxrwx 0 root root  9 1970-01-01 01:00 libm.so.6
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libX11.so.6
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libXaw.so.7
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libXft.so.2
-rwxrwxrwx 0 root root 15 1970-01-01 01:00 libxkbfile.so.1
-rwxrwxrwx 0 root root 11 1970-01-01 01:00 libXmu.so.6
-rwxrwxrwx 0 root root 15 1970-01-01 01:00 libXrender.so.1
-rwxrwxrwx 0 root root 10 1970-01-01 01:00 libXt.so.6

  • visualiser le header ELF à-la readelf -h (fichier /mnt/elf/info)

$ cat /mnt/elf/info
Ident:                             7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
Version:                           1
Class:                             64
Type:                              EXEC (Executable file)
Version:                           1
ELF Header size:                   64 bytes
Entry point:                       0x402550
Program Header offset:             64 bytes
Program Header entry size:         56 bytes
Number of Program Header entries:  9
Section Header offset:             40768 bytes
Section Header entry size:         64 bytes
Number of Section Header entries:  28
SH string table index:             27

  • lister les sections, dans /mnt/elf/sections : bss, data, symtab, dynsym, etc

$ ls -l /mnt/elf/sections/
total 0
drw------- 1 root root 0 1970-01-01 01:00 bss
drw------- 1 root root 0 1970-01-01 01:00 ctors
drw------- 1 root root 0 1970-01-01 01:00 data
drw------- 1 root root 0 1970-01-01 01:00 dtors
drw------- 1 root root 0 1970-01-01 01:00 dynamic
dr-------- 1 root root 0 1970-01-01 01:00 dynstr
dr-------- 1 root root 0 1970-01-01 01:00 dynsym
[...]

  • lister les symboles dans /mnt/elf/sections/symtab et /mnt/elf/sections/dynsym

$ ls -l /mnt/elf/sections/dynsym/|tail -5
d--------- 1 root root 0 1970-01-01 01:00 XtStrings
d--------- 1 root root 0 1970-01-01 01:00 XtWidgetToApplicationContext
d--------- 1 root root 0 1970-01-01 01:00 XtWindow
d--------- 1 root root 0 1970-01-01 01:00 XtWindowOfObject
d--------- 1 root root 0 1970-01-01 01:00 XUnionRectWithRegion

  • pour chaque symbole, lire ses infos (STT_FUNC, etc) et son code, au format binaire ou asm (code.bin et code.asm)... mieux vaut que le binaire soit compilé en debug, ou du moins avec les symboles, et non stripé, sinon vous allez vous retrouvez en galère d'infos

$ ls -l /mnt/elf/sections/symtab/dup_regex_free/
total 0
---------- 0 root root 363 1970-01-01 01:00 code.asm
---------- 0 root root  78 1970-01-01 01:00 code.bin
---------- 0 root root  69 1970-01-01 01:00 info

$ cat /mnt/elf/sections/symtab/dup_regex_free/code.asm
push rbp
mov rbp, rsp
sub rsp, 0x10
mov [rbp-0x8], rdi
mov rax, [rbp-0x8]
mov rax, [rax+0x48]
test rax, rax
jz 0x35
mov rax, [rbp-0x8]
mov rax, [rax+0x48]
mov rdi, rax
call dword 0xfffffd9c
mov rax, [rbp-0x8]
mov qword [rax+0x48], 0x0
mov rax, [rbp-0x8]
mov eax, [rax+0x50]
test eax, eax
jz 0x4c
mov rax, [rbp-0x8]
mov rdi, rax
call dword 0xfffffeac
leave 
ret 

Ouala pour l'instant c'est tout....

J'essaie d'ajouter des trucs quand j'ai le temps mais bon, d'une part j'suis pas mal occupé à faire des trucs pas intéressants, et en plus je code comme une buse et à la lenteur d'un cloporte arthritique, c'est pas gagné...

Le code : c'est par ici !

Bref, see ya dudes!

Ah j'oubliais un truc important : c'est Linux-only... hey ouais, j'ai pas trouvé de truc plus malin que checker /proc/pid/exe pour avoir l'adresse de mapping du binaire depuis le PID. On me souffle dans l'oreille de faire des trucs sioux avec ptrace() + les registres, qui donnent un emplacement dans le code, puis checker en arrière les adresses modulo _sysconf(_SC_PAGESIZE) pour essayer de retomber sur les 4 bytes ELF_MAGIC du header. Plus tard plus tard... déjà on va essayer de faire en sorte que ça SIGSEGV pas trop :P

EDIT: hop, apparemment maintenant ça compile/tourne sous freebsd (j'ai pas pu tester mais le bon sbz m'a envoyé un patch qui fait le taff)

lundi 20 août 2012

Le lapsus du jour: failvault2

Un paper marrant sur le fonctionnement de l'implementation de filevault2 sous OS/X, interessant pour voir comment sont derives les user keys, les recovery keys et comme ils se faisaient chier, ils ont decide de fournir une lib pour mounter/acceder a ton volume CoreStorage/Filevault depuis ailleurs ou ecrire tes tools avec, a lire, vraiment! :)

Enjoy!

jeudi 7 juin 2012

On est encore là, prêts à foutre le souk tout l'monde est cor-da !

Une petite news pour dire qu'on vit encore, et j'en profite pour vous vendre un tool bien sympathoche.

Un collègue de bureau m'a montré l'existence de cppcheck, et je dois dire que c'est pas mal du tout, ce truc.

C'est un outil d'analyse statique pour du C/C++. Il est intégré (ou intégrable) à des IDE, pour ceux qui s'en servent, pour plus de facilité d'utilisation.

Ça sort des rapports au format xml/html faciles à exploiter, il y a relativement peu de faux positifs, trouve aisément des memory leaks, etc.

Bref, une petite passe sur chaque projet aide à trouver des bugs en très peu de temps.

Ouala, c'est tout, enjoy ! :)

Edit : il y a aussi l'utilitaire de la suite clang, le bien nommé `scan-build'. Dans un repository il suffit de taper `scan-build make' puis `scan-view <nom du rapport>'. Ça marche bien et c'est un bon complément à cppcheck.

mardi 20 mars 2012

Dtrace et les gens du voyage...

Ok j'ai dû chercher, alors si je peux faire gagner un peu de temps à d'autres âmes perdues comme la mienne! J'avais envie de pouvoir sniffer les i/o sur n'importe quel process avec des filtres genre sur les file descriptors (ou FD), sur le type de FD ou sur le nom du process, etc.. histoire de voir dans les protocoles de communication locaux (genre via socket unix de pute de merde $@#!$!@#) comment un daemon communique avec son environnement ou comment (exemple d'OS/X) un programme peut communiquer avec le kernel, oui oui sous OS/X ça "peut" se passer via une socket() et pas via un /dev/bla, ioctl() etc.. ca existe aussi mais dans mon cas c'était dans mon cul... bref...

Donc j'ai commencé par ecrire un LD_PRELOAD, mais ce n'est pas le sujet, je me suis amusé avec dtrace(1) pour voir; pas mal d'outils sous OS/X utilisent dtrace(1), comme "dtruss(1m)" (fourni par default sous OS/X), je vous laisse chercher pour tous les trouver, mais voila un premier pas:

J'en reviens a mon process, je sais pas ce qu'il fait, j'aimerais voir ses read()/write(), je passe un coup d'"lsof" (aka LiSt Open Files) pour voir les FDs (File Descriptors, je me répète et j'en vois plein... alors je me dis merde, j'aimerais bien voir où il écrit, juste voir ce qui se passe sur ce FD, etc... je cherche un peu et je tombe sur:

Content, je m'en vais essayer, mais deception...:

# rwsnoop -p 377
  UID    PID CMD          D   BYTES FILE
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx
  501    377 iTerm        R      42 ptmx

Beuh il me print pas le contenu du read()/write(), genre un bel hexdump aurait été bien agréable, ensuite je ne peux pas filtrer par file descriptor (genre je veux voir juste le #6, je sais ce que c'est $#@!$@#!) et j'aimerais aussi pouvoir voir (quand ça n'est pas un fichier) le type de socket (vu que le type n'est pas fourni par la variable interne dtrace "fds"), histoire de savoir ce qui se passe vraiment et pas croire que c'est une vnode alors qu'en fait c'est une socket à la noix... bref...

Donc la première étape, c'est de dumper le contenu du read()/write() et le return code, c'est parti... je vais expliquer rapidement:

syscall::read:entry
/<predicate>/
{
    self->fd = arg0;
    self->rarg1 = arg1;
    self->rarg2 = arg2;
[...]
}

En gros à l'entrée de read() je sauve les arguments, oui je veux afficher mon code de retour et le read() n'a pas encore été exécuté donc le buffer est toujours vide... du coup :

syscall::read:return
/<predicate>/
{
    buffer = copyin(self->rarg1, arg0);
    tracemem(buffer, $opt_dumpsz);
    printf(\"\n\");
}

donc la à la fin de l'exec, je prends le code de retour (arg0), je copie tout ca dans un buffer local (oui oui on est dans l'addressage du noyau là...) et j'affiche le contenu de la mémoire avec tracemem() tout joli en fonction du type, mais si comme moi vous préférez TOUJOURS avoir un hexdump, magie, simplicité, élasticité... on rajoute ça au debut de son script:

#pragma D option rawbytes

voilà là on voit le contenu avec ce format:

             0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  0123456789abcdef
         0: 03 4f 42 7e 3b 39 35 3b 63 a8 11 16 01 00 00 00  .OB~;95;c.......
        10: 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................
        20: 36 00 18 00 00 00 00 00 f0 f0 52 00 01 00 00 00  6.........R.....
        30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

autre galère, retrouver le type de FD, apres avoir parcouru, diverses choses, je conseille donc aux gens qui se cassent la tête, de farfouiller dans ces liens:

je finis par aller voir si dans les struct du noyau je peux accéder à la struct proc (oui elle contient les FDs du process!!), je resume vite fait et simplement il faut passer par la struct task puis récupérer la struct proc (via bsd_info de la structure task), struct filedesc, blablabla jusqu'à la struct fileglob et on finit par voir que la struct proc est accessible via curproc dans dtrace(1), apres du déréferencement à la main on arrive à ça:

curproc->p_fd->fd_ofiles[<FD_num_as_index_of_this_array>]->f_fglob->fg_type

c'est un int avec pour valeurs possibles:

/* file types */
 145#define DTYPE_VNODE     1       /* file */
 146#define DTYPE_SOCKET    2       /* communications endpoint */
 147#define DTYPE_PSXSHM    3       /* POSIX Shared memory */
 148#define DTYPE_PSXSEM    4       /* POSIX Semaphores */
 149#define DTYPE_KQUEUE    5       /* kqueue */
 150#define DTYPE_PIPE      6       /* pipe */
 151#define DTYPE_FSEVENTS  7       /* fsevents */
 152

And voilà....!! maintenant vous reste plus qu'à faire l'amas de ces informations et hop vous avez vos filtres dans votre gentil script dtrace(1)! :)

Maintenant répétez les operations pour avoir filename, socket endpoint, en fonction du type, etc... vous aurez tout ce que vous voulez.

Bon je suis fatigué d'écrire ce truc... j'espère que ça servira à quelqu'un, je retourne lire mes trucs de glands...

jeudi 16 février 2012

Stop! Hamming time!

Dans la même veine qu'un précédent ticket, j'avais besoin de faire une tâche particulière : calculer la distance de Hamming entre plusieurs fichiers, dans un objectif absolument top secret de ouf.

Contrairement à fdup(1), cette fois j'ai (rapidement) cherché dans les dépôts debian/ubuntu si un truc existait déjà. En regardant un peu kdiff3, xdelta, etc, j'ai rien trouvé de bien satisfaisant, alors je l'ai codé. C'est absolument bidon, je préviens, mais ptet que ça servira à quelqu'un, sait-on jamais...

C'est par ici : hamming.

lundi 13 février 2012

Ah ah ! Je ne suis pas dupe !

Bon, ce week end je me faisais passablement chier, et en explorant le contenu de mon $HOME je me suis rendu compte que c'était un peu le bordel, quand même. Des fichiers inutiles, des doublons, etc.

Du coup, illumination, le doigt du génie a effleuré mon front obtus l'espace d'un instant : et si je codais un truc-machin pour repérer tous les fichiers identiques sur ma machine ?!

Et voilà, fdup est né (c'est du C, parce que comme je m'embêtais je voulais pas faire un "one-liner" en shell avec find(1) + awk(1), faut bien s'occuper) ! C'est super minimaliste pour l'instant, hein, y'a genre 2-3 options qui se tirent la bourre, et pis c'est tout.

Je vous entends ricaner d'ici : bien sûr qu'il y a d'autres outils qui font le même boulot (fdupes(1), duff, y'a même un fdup déjà présent sur github -- bon, la description dans le README est un peu nawak mais on s'en fout !), mais ça, je m'en suis aperçu qu'après mon premier commit. Comme d'hab'.

mardi 18 octobre 2011

ahhh il reste des gens drôles! pfiiiouuu j'ai flippe!

En ces temps de gang bang d'ego généralisé, de benchmarking de performance sur l'échelle Ritchie (RIP) et autre courses a la réussite sociale, il reste des gens dont la carriere et la publication frénétique d'information pour voir son nombre de hits et ses fans augmenter toute en mimant le désintérêt et le detachement n'est pas au centre de leur vie et arrivent a s'amuser et surtout a avoir de l'auto derision, ca m'a fait hurler de rire alors que c'est completement debile, mais c'est pas grave, j'ai ris, ca fait du bien! donc je "share" :)

Sa premiere idee farfelue (hahahaha):

/*
** pam_happy_hour.c - PAM happy hour auth module. Solaris 2.6+
**      During happy hour passwords aren't required, any will do.
**
[...]

pam_happy_hour.c

Allez j'en pioche une autre dans le tas (:~)):

# maybe - companion to /usr/bin/true and /usr/bin/false.
#
# This should be installed in your system location, eg /usr/bin/maybe.
[...]

maybe

Et il en a fait pleins d'autre aussi farfelue a voir la:

huhuhuh

vendredi 7 octobre 2011

Mon Toy-project du moment ! Un FS sur le cloud !

Beh voilà, encore un peu d'auto-promotion. Et en fait j'ai un peu menti, ça fait plusieurs mois que je ne commit qu'un bout de code par-ci ou par-là.

Ma société a écrit une bibliothèque en open source pour travailler sur le cloud (différents protocoles supportés, mais je vous laisse découvrir ça). Bref, je me suis basé sur cette lib pour implémenter un filesystem en utilisant FUSE : dropletfs ; c'est lent (car le design est simpliste et pas optimisé), mais rigolo.

Toutes les features d'un FS posix ne sont pas présentes, loin de là (gérer les hardlinks est plutôt compliqué), mais pour un usage basique, ça 'juste marche'.

Une petite démo intéractive (nécessite le package ttyrec) : ici

samedi 18 juin 2011

des piécettes électroniques! donc l'avènement d'e-mendiants!

Apres quelques conversations avec un pote sur cette nouvelle "monnaie" électronique, il m'a fait constater un ensemble de propriétés marrantes et interessantes (pas de trusted third party, la resolution d'un problème mathématique pour limiter l'emission de monnaie, etc..), j'ai beaucoup de lacunes quand au fonctionnement économique actuel, mais le fonctionnement de cette monnaie et l'utilisation de la crypto et du réseau pour definir ces propriétés, a lire et suivre!

jeudi 17 février 2011

Des hooks sympa de fonctions avec gcc

Yo yo yo.

J'avais une base de code, avec pas mal de fonctions déjà implémentées, et puis je me suis dit "ooooomm, faudrait que je trace le temps d'exécution de chacune de ces fonctions, pour savoir où concentrer mes efforts, en faisant telle et telle stat'". Bon alors je vous entends déjà barir au loin "hey mais pauvre naze, y'a plein d'outils de profiling", et c'est vrai. Seulement je voulais faire un truc à ma sauce d'une part (histoire de voir un peu comment ça fonctionne), et surtout pouvoir ajouter les stats qui m'intéressent, faire des trucs aux p'tits oignons quoi. Et puis c'est pas comme si ça prenait des jours à coder.

Alors après avoir cherché un peu, je tombe sur la doc GCC (même le man en parle, à vrai dire) :


void __cyg_profile_func_enter(void *func, void *callsite); 
void __cyg_profile_func_exit(void *func, void *callsite);


Ces fonctions sont appelées respectivement à l'entrée et la sortie de chacune des fonctions de votre programme. Il y a une linuxerie ensuite pour obtenir le nom de l'appelant, ie. passer par dladdr() ou __build_return_address(0). Ensuite vous stockez les infos comme vous voulez, perso j'utilise une hashtable dont les clefs sont les symboles (aka noms de fonctions), avec chaque cellule contenant des stats, sur le nombre d'appel, le temps d'exécution moyen, etc.

J'ai utilisé ça pour un code sur lequel je passe un peu de temps en ce moment, et la sortie ressemble à (le > correspond à l'entrée dans la fonction, le < à la sortie, faudrait rendre le truc plus joli avec un système de pile pour avoir la profondeur d'appel de chacune en fonction d'un décalage horizontal, mais bon, ça viendra après) :


[...]
1297941768.719531 > dfs_mkdir@0x40f534
1297941768.719607 > dfs_mkdir_timeout@0x41398f
1297941769.416415 < dfs_mkdir_timeout@0x41398f -- 696ms
1297941769.416445 < dfs_mkdir@0x40f534 -- 696ms
1297941769.416469 > dfs_getattr@0x40c16c
1297941769.416684 > dfs_namei_timeout@0x413ef7
1297941769.534098 < dfs_namei_timeout@0x413ef7 -- 117ms
1297941769.534161 > dfs_getattr_timeout@0x413690
1297941770.104592 < dfs_getattr_timeout@0x413690 -- 570ms
1297941770.104874 < dfs_getattr@0x40c16c -- 570ms

                                -- report --
symbol dfs_getattr_timeout: #calls: 46, average call duration: 266ms
symbol dfs_opendir_timeout: #calls: 1, average call duration: 780ms
symbol dfs_namei_timeout: #calls: 47, average call duration: 299ms
symbol dfs_chdir_timeout: #calls: 1, average call duration: 0ms
symbol dfs_mkdir: #calls: 1, average call duration: 696ms
symbol dfs_getattr: #calls: 48, average call duration: 275ms
symbol dfs_mkdir_timeout: #calls: 1, average call duration: 696ms


Le code (crados, pas encore nettoyé) est visible ici. Comme je disais, le truc vraiment sympa c'est juste d'avoir à appeler profile_init() et profile_fini() dans mon code. Et encore, on peut faire mieux en générant un .so qu'on charge avec LD_PRELOAD, histoire d'avoir vraiment 0 code impacté. Faut juste compiler les objets avec -fPIC et -finstrument-functions. That's all folks.

OK c'est un profiler du pauvre, mais ici à unix4fun, on est prolos. Et on vous emmerde !

mardi 9 novembre 2010

Mon compilo est un beau salaud!

Je lis du code généré en ce moment... en particulier du code généré par Visual C++ avec des options "star-wars-security" blablabla mais c'est pas le sujet du jour...

Le sujet du jour c'est ma relative incompétence face a des choses comme:

[...]
.text:0069979F mov     eax, 66666667h          
.text:006997A4 imul    ecx
.text:006997A6 sar     edx, 1                          
.text:006997A8 mov    eax, edx                     
.text:006997AA shr     eax, 1Fh                       
.text:006997AD add     eax, edx
[...]

alors dans ecx, j'ai la valeur 0x20000, après des tentatives rapides de compréhension, je me suis bon les "magic values", j'en vois de temps en temps, ca pue la "magic value" des trucs de compilo-salaud pour tenter d'avoir de la précisions en continuant a travailler avec des entiers et en base2, bref..

En cherchant un peu je suis tombe la dessus:

Et j'ai enfin compris ce que ce petit snippet de code faisait, alors d’après vous il se passe quoi ? et quel est l’algèbre applique a la valeur dans ecx!? :) (je mettrais a jour avec une explication si le lien n'est vraiment pas clair...)

peace!

jeudi 4 novembre 2010

Le strict aliasing en C

Un billet assez exhaustif, avec des exemples de code et d'output assembleur : c'est ici

jeudi 7 octobre 2010

Pwnage par libpcap

Yo. Dans ce post je vais expliquer comment je me suis battu pour faire un truc assez simple avec la libpcap sous linux.

Au début était un sniffer, je voulais écouter sur une interface à grands coups de SOCK_RAW + recvfrom(). À l'ancienne, quoi. L'idée était de ne pas utiliser de handler pcap (pcap_t *). Au bout d'un moment je me dis "tiens, ça serait sympa d'ajouter les filtres BPF comme dans tcpdump". Brillante idée, non ? Non, ok. Bref, le code de départ est le suivant :

// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// vim:sw=4 ts=4 sts=4 expandtab
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>
#include <netpacket/packet.h>
#include <poll.h>
#include <linux/types.h>
#include <errno.h>
#include <pcap.h>
#include <linux/filter.h>

#define FRAME_SIZE 65536

/* iface_setprom() sets "device" in promiscuous mode */
static int
iface_setprom(int sock_fd, const char *device)
{
    struct ifreq ifr;

    memset(&ifr, 0, sizeof ifr);
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", device);

    if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
        printf("[%s] ioctl: %s.\n", device, strerror(errno));
        return -1;
    }

    ifr.ifr_flags |= (IFF_PROMISC | IFF_UP);

    if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
        printf("[%s] ioctl: %s.\n", device, strerror(errno));
        return -1;
    }

    printf("[%s] enter in promiscuous mode\n", device);
    return 0;
}



À partir d'une chaîne de caractère entrée, par exemple "tcp and port 22", on veut obtenir une structure comprise par pcap_compile*(). Comme dit précédemment, je ne veux pas de pcap_t* à manipuler, je vais donc utiliser pcap_compile_nopcap() (non mentionné dans le man de pcap_compile sous linux, évidemment).

/* iface_setfilter() applies a LSF filter to the file descriptor */
static int
iface_setfilter(int sock_fd, const char * const device, const char * const filter)
{
    if (!filter || !*filter) {
        printf("[%s] no filter to install\n", device);
        return 0;
    }

    struct bpf_program bpf;
    memset(&bpf, 0, sizeof bpf);

    char errbuf[PCAP_ERRBUF_SIZE = "";

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof bpf)) {
        printf("[%s] setsockopt: %s (filter '%s')\n", device, strerror(errno), filter);
        return -1;
    }

    printf("[%s] filter '%s' successfully installed\n", device, filter);
    return 0;
}

/* iface_init() is the main device initialization function */
static int
iface_init(struct pollfd *fds, const char * const device, const char * const filter)
{
    printf("Initializing interface %s (filter '%s')\n", device, filter);

    fds->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (fds->fd == -1) {
        printf("[%s] socket: %s. Exiting...\n", device, strerror(errno));
        exit(EXIT_FAILURE);
    }

    fds->events = POLLIN | POLLPRI;

    if (0 != iface_setprom(fds->fd, device)) return -1;
    if (0 != iface_bind(fds->fd, device)) return -1;
    if (0 != iface_setfilter(fds->fd, device, filter)) return -1;

    return 0;
}

static void
frame_manager(const char * const device, struct pollfd *fd, struct timeval const *now)
{
    uint32_t i;

    if ((fd->revents & (POLLIN | POLLPRI)) == 0)
        return;

    unsigned char buf[1500] = "";
    ssize_t n = recvfrom(fd->fd, buf, sizeof buf, MSG_DONTWAIT, NULL, NULL);
    if (n < 0) {
        printf("[%s] recvfrom: %s (i=%d, fd=%d)\n", device, strerror(errno), i, fd->fd);
        return;
    }

    printf("[%d,%d] packet received\n", (int)now->tv_sec, (int)now->tv_usec);
}

int main(int ac, char **av)
{
    char *filter = "";
    char *device = "dummy0";

    if (ac > 1) {
        filter = av[1];
    }

    struct pollfd fd;
    (void)iface_init(&fd, device, filter);

    while (/*CONSTCOND*/ 1) {
        if (poll(&fd, 1, -1) > 0) {
            struct timeval now;
            gettimeofday(&now, NULL);
            frame_manager(device, &fd, &now);
        }
    }

    close(fd.fd);
    return EXIT_SUCCESS;
}



Tout d'abord, je monte une interface virtuelle pour éviter le bruit généré par les paquets circulants sur mes vraies interfaces :

$ sudo modprobe dummy
$ sudo ifconfig dummy0 mtu 1600 up



Maintenant, si je compile et j'exécute le code, et que je balance du ssh sur dummy0, j'ai ça :

$ cc -o cap cap.c -lpcap
$ sudo ./cap "tcp and port 22"
[...]



Et dans un autre terminal :

$ sudo tcpreplay -i dummy0 ssh.pcap



Mais aucun "packet received" ne s'affiche. Damned ! En inspectant un peu le code, on voit ça :

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof bpf)) {



Ça a l'air bien comme ça, hein. Malheureusement, le 'struct bpf_program' n'est pas compris sous linux, qui, ne voulant pas faire comme ses potes *BSD attend un 'struct sock_fprog *' en avant dernier argument. Bon, ici je me dis "il doit bien y avoir une fonction qui fait la conversion, cherchons dans le source de libpcap". En jetant un coup d'oeil rapide, on tombe sur ça :

libpcap-0.9.8/pcap-linux.c:215 static int       fix_program(pcap_t *handle, struct sock_fprog *fcode);



Je suis content, mais visiblement je dois passer par un pcap_t. Du coup, je reprends le code en l'adaptant pour taper directement dans la structure interne du handler pcap, et j'écris la fonction suivante :

#define SLL_HDR_LEN 16
static int lcc = 0; // linux cooked capture

static int
fix_offset(struct bpf_insn *p)
{
    if (p->k >= SLL_HDR_LEN) {
        p->k -= SLL_HDR_LEN;
    } else if (p->k == 14) {
        p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
    } else {
        return -1;
    }
    return 0;
}

static int
fix_code(struct bpf_program *bpf, struct sock_fprog *fcode)
{
    size_t prog_size;
    register int i;
    register struct bpf_insn *p;
    struct bpf_insn *f;
    int len;

    len = bpf->bf_len;
    prog_size = sizeof(bpf->bf_insns) * len;
    f = malloc(prog_size);
    if (f == NULL) {
        printf("malloc: %s\n", strerror(errno));
        return -1;
    }
    memcpy(f, bpf->bf_insns, prog_size);
    fcode->len = len;
    fcode->filter = (struct sock_filter *)f;

    for (i = 0; i < len; ++i) {
        p = &f[i];
        switch (BPF_CLASS(p->code)) {

        case BPF_RET:
            if (BPF_MODE(p->code) == BPF_K) {
                if (p->k != 0)
                    p->k = 65535;
            }
            break;

        case BPF_LD:
        case BPF_LDX:
            switch (BPF_MODE(p->code)) {

            case BPF_ABS:
            case BPF_IND:
            case BPF_MSH:
                if (lcc) {
                    if (fix_offset(p) < 0) {
                        return 0;
                    }
                }
                break;
            }
            break;
        }
    }
    return 1;       /* success! */
}



Maintenant je réécris ma fonction iface_setfilter() pour convertir le code BPF :

static int
iface_setfilter(int sock_fd, const char * const device, const char * const filter)
{
    if (! filter || ! *filter) {
        printf("[%s] no filter to install\n", device);
        return 0;
    }

    struct bpf_program bpf;
    memset(&bpf, 0, sizeof bpf);

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }

    struct sock_fprog fcode;
    memset(&fcode, 0, sizeof fcode);
    int ret = fix_code(&bpf, &fcode);

    if (1 != ret) {
        printf("[%s] fix_code failed (rcode = %d) for filter '%s'\n", device, ret, filter);
        return -1;
    } else {
        printf("[%s] fix_code worked (rcode = %d) for filter '%s'\n", device, ret, filter);
    }

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &fcode, sizeof fcode)) {
        printf("[%s] setsockopt: %s (filter '%s')\n", device, strerror(errno), filter);
        return -1;
    }

    printf("[%s] filter '%s' successfully installed\n", device, filter);
    return 0;
}



Là, logiquement je me dis "Youpi les knakis¸ ça va marcher !". Mais si je reteste, toujours rien. Rien ne s'affiche. La blague est même que si j'invoque :

$ sudo ./cap "not port 80"



... et que je joue du traffic http (sur le port 80 bien sûr), mon outil logue qu'il a vu un paquet ! Par contre avec "port 80" il n'affiche rien. Bon je me suis pas mal gratté la tête ici, j'avoue. Du coup j'appelle mon copain strace à la rescousse, et je compare le raw des filtres passés à setsockopt() entre mon code et l'appel tcpdump :

$ sudo strace -s 256 -f -v ./cap "port 80" 2>&1 | grep ATTACH_FILTER
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\33\0\0\0\0\0\0\0\20\240\363\0\0\0\0\0", 16) = 0
^C
$ sudo strace -s 256 -f -v tcpdump -ni dummy0 port 80 2>&1 | grep ATTACH_FILTER
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\1\0\0\0\0\0\0\0h\303\315E(\177\0\0", 16) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\30\0\0\0\0\0\0\0\360V1G(\177\0\0", 16) = 0
^C



Les outputs ne sont pas les mêmes. La génération du filtre est donc foireuse, je vérifie maintenant comme suit :

$ sudo tcpdump -nd port 80
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 10
(002) ldb      [20]
(003) jeq      #0x84            jt 6    jf 4
(004) jeq      #0x6             jt 6    jf 5
(005) jeq      #0x11            jt 6    jf 23
(006) ldh      [54]
(007) jeq      #0x50            jt 22   jf 8
(008) ldh      [56]
(009) jeq      #0x50            jt 22   jf 23
(010) jeq      #0x800           jt 11   jf 23
(011) ldb      [23]
(012) jeq      #0x84            jt 15   jf 13
(013) jeq      #0x6             jt 15   jf 14
(014) jeq      #0x11            jt 15   jf 23
(015) ldh      [20]
(016) jset     #0x1fff          jt 23   jf 17
(017) ldxb     4*([14]&0xf)
(018) ldh      [x + 14]
(019) jeq      #0x50            jt 22   jf 20
(020) ldh      [x + 16]
(021) jeq      #0x50            jt 22   jf 23
(022) ret      #96
(023) ret      #0



Et j'invoque

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }
    bpf_dump(&bpf, 0);



J'ai en sortie :

Initializing interface dummy0 (filter 'port 80')
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
(000) ldb      [0]
(001) and      #0xf0
(002) jeq      #0x60            jt 3    jf 11
(003) ldb      [6]
(004) jeq      #0x84            jt 7    jf 5
(005) jeq      #0x6             jt 7    jf 6
(006) jeq      #0x11            jt 7    jf 26
(007) ldh      [40]
(008) jeq      #0x50            jt 25   jf 9
(009) ldh      [42]
(010) jeq      #0x50            jt 25   jf 26
(011) ldb      [0]
(012) and      #0xf0
(013) jeq      #0x40            jt 14   jf 26
(014) ldb      [9]
(015) jeq      #0x84            jt 18   jf 16
(016) jeq      #0x6             jt 18   jf 17
(017) jeq      #0x11            jt 18   jf 26
(018) ldh      [6]
(019) jset     #0x1fff          jt 26   jf 20
(020) ldxb     4*([0]&0xf)
(021) ldh      [x + 0]
(022) jeq      #0x50            jt 25   jf 23
(023) ldh      [x + 2]
(024) jeq      #0x50            jt 25   jf 26
(025) ret      #65536
(026) ret      #0
[dummy0] fix_code worked (rcode = 1) for filter 'port 80'
[dummy0] filter 'port 80' successfully installed
^C



On voit bien que c'est différent au début. Le reste est plutôt cohérent

(016) jeq      #0x6             jt 18   jf 17  # 0x06 c'est l'IPPROTO_TCP (6)
(017) jeq      #0x11            jt 18   jf 26  # 0x11 = 17 = IPPROTO_UDP



Si on n'est dans aucun des deux cas, on saute en 26 (jf 26), soit la fin. Sinon, on regarde si le port vaut 80 (0x50) :

(022) jeq      #0x50            jt 25   jf 23
(023) ldh      [x + 2]
(024) jeq      #0x50            jt 25   jf 26



Le problème est vraiment le prologue. Et là je on me souffle à l'oreille que c'est peut-être le datalink type. Réaction: "bon dieu de !#@@!#@!# mais c'est bien sûr, ça doit être mon datalink (DLT) qui est moisi !" Si j'écris :

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_IEEE802, &bpf, filter, 1, 0)) {



J'obtiens en sortie :

$ cc -o cap cap.c -lpcap && sudo ./cap "port 80" 
Initializing interface dummy0 (filter 'port 80')
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
(000) ldh      [20]
(001) jeq      #0x86dd          jt 2    jf 10  # 0x86dd est l'ethertype IPv6
(002) ldb      [28]
(003) jeq      #0x84            jt 6    jf 4
(004) jeq      #0x6             jt 6    jf 5
(005) jeq      #0x11            jt 6    jf 23
(006) ldh      [62]
(007) jeq      #0x50            jt 22   jf 8
(008) ldh      [64]
(009) jeq      #0x50            jt 22   jf 23
(010) jeq      #0x800           jt 11   jf 23  # 0x800 est l'ethertype IPv4
[...]



Mais toujours rien n'est logué si je joue du http ! En remplaçant DLT_IEEE802 par DLT_EN10MB en revanche...

[...]
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
[dummy0] fix_code worked (rcode = 1) for filter 'port 80'
[dummy0] filter 'port 80' successfully installed
[1286457559,615193] packet received



Hallelujah ! Hosanna hosanna !

Enfin bon, c'est quand même moche d'écrire le DLT en dur dans le code, donc je l'ai récupéré via la libpcap (mais on est obligé de passer par un handler pcap_t *, ce que je ne voulais pas trop au début...)

    char errbuf[256] = "";
    pcap_t *p = pcap_open_live(device, FRAME_SIZE, 1, 0, errbuf);
    if (! p) {
        printf("[%s] pcap_open_live failed: %s\n", device, errbuf);
        return -1;
    }

    int dlt = pcap_datalink(p);
    pcap_close(p);

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, dlt, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }



En fait, comme on dispose du handler pcap_t, on peut même dégager pcap_compile_nopcap() et utiliser pcap_compile(), qui lui n'a même pas besoin d'un DLT en paramètre... Tout ça parce que j'ai voulu me passer de ce foutu handler, en somme. Bref le code est disponible ici

mercredi 15 septembre 2010

GCC ifunc!

Pendant ma petite pause lecture du matin, je suis tombé sur ce post concernant les nouvelles updates de la GNU toolchain, et en particulier un nouvel attribut: "ifunc"

Ca permet de définir dynamiquement une fonction qui va "résoudre" vers la fonction de votre choix en fonction de critères que vous pouvez définir, le monsieur @ http://nickclifton.livejournal.com/6612.html donne un exemple très simple et très clair (plus clair que mes explications foireuses) que je reproduirais ici :

void *slow_memcpy (void *dst, const void *src, size_t len)
{
         char *d = dst; char *s = src;

         while (len--)
           *d++ = *s++;

         return dst;
}

void *fast_memcpy (void *dst, const void *src, size_t len)
{
         __asm("foo %0 %1 %2" : "=m" (dst) :  "m" (src), "r" (len) : memory);
         return dst;
}

static void (* resolve_memcpy (void)) (void)
{
         return __cpu_has_foo () ? fast_memcpy : slow_memcpy;
}

void *memcpy (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy")));

Les details chez http://nickclifton.livejournal.com/6612.html

lundi 16 août 2010

si toi aussi tu aimes le poulet routi!

Je suis une grosse feignasse et je lis pas énormément de code, en ce moment je lis des trucs fait sur de l'ARM. En bonne grosse tanche, je n'avais jamais vu ces CMP suivi d'un CMPNE et d'un BXX ou un MOVXX etc.. (c'est en mode ARM only) ailleurs:

CMP      R1,#1 
CMPNE    R2,#2 
BEQ      bleh 

Heureusement qu'il y a l'internet poilu pour éclairer et donner des belles recettes.

J'ai trouve des gens qui parlent de comment on fait un poulet roti et comment voir si il est bien cuit!

Apparemment tu peux chainer des conditions tranquillement avec un petit "suffix" dans l'instruction (il y a des bits réservés dans l'instruction pour ces options ARM-only)

voila ca m'a rendu service aujourd'hui enjoy!

mercredi 9 juin 2010

Le crochetage SSL avec ton serpent

Pour un premier post, désolé, c'est sur du win32 :). Je lisais dernièrement "GrayHat Python" et je découvre Pydbg (un debugger écrit en python) que je ne connaissais pas (bon je ne connais pas grand chose non plus, du coup c'est plus facile d'être émerveillé) et dans le chapitre sur le hooking je tombe sur un exemple de "soft-hooking" (le hooking avec un breakpoint soft (INT3) sur la fonction qui t'intéresse) pour "sniffer" les connections SSL dans firefox (hook sur une fontion specifique de firefox).

Ca me rappelle des discussions avec eau qui avait déjà commence a bosser dessus, en voyant comment le truc est tout con ca m'a donne envie de voir si hooker les "CryptoAPI"s de MS que IE utilise est tout aussi simple.

Donc une fois recup Pydbg, un petit "import pydbg" et on peut instancier un object debugger mydbg=pybdg.pydbg().
Et voila! maintenant qu'on a notre debugger instancié, on peut s'attacher a des process, mettre des breakpoints, lire l'etats des registres etc.. Un peu comme scripter a l'interieur d'IDA ou ImmunityDebugger.

Du coup c'est vrai que le soft hooking comme ca c'est tout bidon. Un petit breakpoint dans la fonction qui nous intéresse, une callback fonction pour effectuer des opérations sur cette fonction et on release le breakpoint pour que le process continue normalement.

Bon, c'est pas efficace du tout côté perf, c'est clair...
Mais pour un ptit hooking rapido histoire de tester/fuzzer un truc ca peut etre sympa et utile.

Alors voila j'ai matte les CryptoAPI de MS et j'ai pense d'abord hooker "CryptEncrypt()" et "CryptDecrypt()" exportées par "Advapi32.dll".

Ok finalement en cherchant un peu, il y a eut pleins de présentations sur le hooking de ces 2 fonctions (par exemple celle-ci) et en effet c'est tout bon mais pour sniffer uniquement.

Si on veut modifier les requêtes envoyées par exemple, alors la on voit qu'on tombe sur un os, en tracant les appels un certain "CryptHashData()" est appelé avant "CryptEncrypt()" et donc dommage.. Difficile de modifier un truc hashe préalablement sans que ca se voit! Et la ce cher eau me dit qu'il avait regarde "EncryptMessage()" et "DecryptMessage()" et il avait raison le bougre!

En effet "EncryptMessage()" prend la requête, appele "CryptHashData()" puis "CryptEncrypt()" et balance tout ca hashé/chiffré ensuite au fonctions résal(réseaux). Du coup hooker "EncryptMessage()" est bien plus intéressant puisqu'avant le hash donc possible de modifier la requête.

Je n'ai pas encore réussi a faire tout ce que je voulais sur "EncryptMessage()", modifier la requête...ok, mais uniquement si sa nouvelle taille est <= a l'originale. Pour en générer une de taille > a la requête originale, je coince.

J'ai essaye de mettre a jour la taille a plusieurs endroits, re-ecrire la requête en allouant assez de place ailleurs et updater les pointeurs/références sur ma nouvelle requête mais niet... je dois merder, quelque chose m'échappe.

Voyant que des fonctions appelées avant "EncryptMessage()" manipulaient aussi la requête HTTP et utilisaient la taille pour différents trucs que je n'ai pas exploré je me suis résigné a hooker la dedans et c'est passé. Enfin, j'ai finalement hooke une fonction qui se trouve dans "Wininet.dll" mais elle semble tres specifique a IE donc pas super fonctionnel pour autre chose qu'IE... (i.e. Thick Client)...

Pour ceux que ca intéresse, voila un petit bout de code tout bidon (et pas beau je sais :)..) mais c'est juste pour tester et voir comment jouer avec pydbg et le soft-hooking:

hook_ssl_pydbg.py

J'ai teste ca sur XP SP2 en anglais avec IE7.

Un petit print screen aussi pour voir ce que ca donne:

hook_ssl_pydbg.png

Pydbg fait parti du framework Paimei (du coup ca m'a donne envi de revoir kill bill2..):
Paimei

svn checkout http://paimei.googlecode.com/svn/trunk/pydbg paimei-read-only

CryptEncrypt(), CryptDecrypt(), EncryptMessage(), DecryptMessage()

Ca passe ok avec python 2.6 sur ma VM XP mais quelques posts disent que pydasm ne passe bien qu'avec python 2.5.

lundi 26 avril 2010

ASM reference

En ce moment je bricole des trucs en assembleur x86/x86-64 et MIPS (wouhouu j'ai un loongson 2f!), alors du coup j'ai qqes liens:

Et j'en profite pour dire que pour tester il y a:

Et il y a aussi qqes petits liens classiques:

vendredi 29 janvier 2010

mechante deadlock! vilaine! booouuu mechante! a la niche!

Un projet marrant et que je trouve super drole, j'ai a peine commence a lire et j ai pas poste depuis des lustres, allez hop c'est la fete :)

Un projet pour "eviter" les deadlock sans avoir a recompiler quoique ce soit, vais tester ca gentillement ce soir!

Le project:

Le paper:

Le code:

jeudi 17 décembre 2009

he HARP project: Have you ever wanted to build a CPU?

Un projet super poilu, qui a pour but de construire une machine entiere "au travers d'"/"via" un FPGA, alors que je commence a peine a comprendre les debut de bases de VHDL et a faire mes premiers debuts d'embryons de tests, de trucs tout moisis, on me souffle ce lien, je vois ca et la pouf, je bande....mais j'y comprends encore rien, je vous rassure..

Le lien erectile:

- page 1 de 4