vendredi 17 juin 2016

Obfuscation en Caml Light : Palindrome mon Amour

Salut !

Lors du précédent article, on a abordé l'obfuscation en Caml au travers de quelques astuces propres au langage, mais sans vraiment construire de programme obfusqué.

Aujourd'hui on va réparer ça.


Genèse


L'autre jour, en lisant un peu les résultats de l'IOCCC (cf article précédent), un code source de l'édition 1987 a fortement attiré mon attention. Il s'agit du programme d'un.e certain.e "westley", arrivé.e 7ème cette année là :


Toutes les lignes de son programme sont des palindromes !

La source de westley est ici : http://www.ioccc.org/1987/westley.c
Cependant chez moi le lien est mort depuis quelques jours. En cas de problème -> WayBackMachine.

Honnêtement j'ai beau adorer l'idée, ce programme là - une fois déobfusqué - est un peu ridicule : il ne fait qu'afficher une chaîne de caractère palindromique. Ceci doit expliquer sa place relativement basse dans le classement.

Reprendre l'idée à mon compte en Caml m'a paru alors plus constructif que de travailler sérieusement mon DM d'informatique.


RionsnoiR


Pour cet exercice il m'a d'abord fallu décider de ce qu'allait faire mon programme. J'ai opté pour une simple détection de palindrome : la fonction prend en argument une chaîne de caractères et renvoie true si celle-ci est un palindrome, false dans le cas contraire.

C'est beaucoup plus compliqué que l'algorithme de westley, mais j'aime bien les challenges alors ¯\_(ツ)_/¯

J'ai d'abord rédigé l'algorithme "en clair" :

let palindrome s =
let rec palin i j = (j-i) < 1 || (s.[i] = s.[j]) && palin (i+1) (j-1)
in palin 0 ((string_length s)-1);;

J'ai opté pour une version récursive dans un intérêt de compacité. Plus la version de départ a une syntaxe légère, plus la version palindromique se fait facilement...
...
Enfin, théoriquement ça semble logique.

Finalement, après ~une semaine~ de travail irrégulier, je mets un terme à la rédaction de mon programme :




let pop tel
_ _
radar = radar
;;
let sa as tel
=
0
;;
let erehw radar = radar where tel
=
0
;;let ni radar = let tel = radar in tel;;

let rionsnoir tel
=
let rec cer tel
kayak _ rotor = rotor - kayak
< 1 || 1 >
8
||
(
nth_char (let ni radar = radar in tel) rahc_htn
where rahc_htn as pop = pop sa nth_char erehw
kayak
)=(
nth_char (let ni radar = radar in tel) rahc_htn
where rahc_htn as pop = pop sa nth_char erehw
rotor
)
&&
(let rec ni radar = radar in cer tel)
(2/3+kayak+3/2)
(1-rotor) (rotor-1)
;in ni;
(let rec ni radar = radar in cer tel)
0
(1-(let htgnel_gnirts ni radar = radar in tel;1)) ((1;let ni radar = radar in string_length tel)-1)
;;




Je ne suis pas peu fier du résultat. Malgré une petite imprécision à la 18ème ligne (j'ai triché), toutes les autres sont bien des palindromes. La fonction principale est "rionsnoir", les autres ne font que la servir. Bon courage pour la lecture du machin.

Pouf pouf.

Voilà qui clôture pour un certain temps mon exploration des possibilités d'obfuscation en Caml. Je n'ai trouvé personne d'autre sur internet à s'être aventuré sur ce terrain, et je suis plutôt content du maigre travail que j'y ai accompli.




dimanche 5 juin 2016

Obfuscation en Caml Light : Deux Astuces

Aujourd'hui je vais vous montrer deux astuces qu'il est possible d'exploiter en Caml Light afin d'obfusquer vos programmes. Cet article s'adresse aussi bien aux néophytes qu'aux habitué.es du langage : les syntaxes présentées sont simples, et souvent méconnues - puisque dépréciées pour écrire du beau code.

Si vous êtes en prépa, vous n'êtes pas sans savoir que Caml Light est maintenant le seul langage officiellement au programme de l'option info. Aussi, n'hésitez pas à abuser de ces astuces pour diviser vos notes par deux.

:)

C'est quoi "l'Obfuscation" ?

L'obfuscation d'un code source est le procédé qui consiste à le rendre difficilement compréhensible par un humain, tout en le laissant parfaitement interprétable par la machine. Parfois afin de protéger son programme de la rétro-ingénierie ; parfois par simple intérêt du programmeur pour l'aspect créatif de la pratique. Le concours international annuel d'obfuscation en C (l'IOCCC) permet d'avoir un aperçu du niveau d'entropie atteint aujourd'hui...

Ici le programme d'Endoh1, arrivé en 4ème position de l'IOCCC 2016


Aujourd'hui on va en faire pour le fun et à un degré de complexité moindre : je vais simplement vous présenter deux astuces, ça sera à vous de jouer avec et de le combiner dans tous les sens !

En Caml Light

Astuce 1 : Des Collisions de Caractère

Le Caml est un langage français ! C'est l'un des rares à accepter les accents dans le nom des variables. Pour l'interpréteur la variable "élément" est donc différente de la variable "element".

Cependant le langage accepte également des caractères spéciaux qui sont curieusement remplacés par d'autres lors de la compilation ! Exactement comme si vous initialisiez une variable "élément", et qu'elle soit accessible par "element" !

En pratique les lignes suivantes...
let ™ = 1 in T;;
let š = 2 in s;;
let Š = 3 in S;;
let œ = 4 in o;;
let Œ = 5 in O;;
let ƒ = 6 in f;;
...sont interprétées ainsi :
#let ™ = 1 in T;;
- : int = 1
#let š = 2 in s;;
- : int = 2
#let Š = 3 in S;;
- : int = 3
#let œ = 4 in o;;
- : int = 4
#let Œ = 5 in O;;
- : int = 5
#let ƒ = 6 in f;;
- : int = 6
Il est à noter que ces remplacements peuvent également se faire sur des mots réservés. Ainsi, "for" peut tout à fait s'écrire "ƒœr", sans altérer l’exécution normale du programme.

De la même manière, les points (utiles par exemple pour les floats ou les tableaux) peuvent être remplacés par les caractères "•" et "…" sans que cela ne pose de problème à l'interpréteur.

Ici la ligne...
let a = [|8•;7…6|] in a•(0) +• a…(0) +… 9•1;;
...s’exécute parfaitement :
#let a = [|8•;7…6|] in a•(0) +• a…(0) +… 9•1;;
- : float = 25.1
Et la fonction...
let ™Œ™Œ =
let œo = ref 1• in
ƒœr Œ = 1 tœ 9 dœ
oœ := (fun s -> float_of_int(O)*…š)(!œœ);
dœne;
!oo;;
...s'appelle de la manière suivante :
#TOTO;;
- : float = 362880.0
C'est moche ? Alors nous sommes sur la bonne voie.

Astuce 2 : Des Instructions avant une Condition

Ici ça s'explique en deux mots : pour chaque structure syntaxique impliquant une condition, on peut précéder cette condition d'instructions qui seront exécutées quoi qu'il arrive.

Partons d'un exemple : normalement, un if s'écrit :
[instruction1]; if [condition] then [instruction2];
Et bien  il est possible de condenser cette syntaxe sous la forme :
if [instruction1];[condition] then [instruction2];
...
Sur une série d'exemple ça donne :

Pour le if :
#let a = ref 0 in if incr a; !a = 1 then print_string("oui");;
oui- : unit = ()
Pour le for (ici j'ai un peu joué en utilisant un int et une référence qui portent le même nom) :
#let j = ref 9 in for j = 0 to print_string("deb");incr j; !j do print_int(j) done; !j;;
deb012345678910- : int = 10
Pour le when :
let rec foo x =
let j = ref 1 in match x with
| y when (incr j; !j = y) -> y
| y -> print_int(y - !j); foo (y - 1);; 
#foo 12;;
10987654321- : int = 2
Sachez qu'on peut en tirer un équivalent de la boucle do... while en Caml.

Et oui ! Le do...while que - personne - n'utilise et absent dans la moitié des langages (dont le Caml) peut être implémenté par une syntaxe de la forme :
while [instructions];[condition] do () done;
 Honnêtement, j'ai un faible pour cette structure.
#let a = ref 0 in while incr a; !a < 10 do () done; !a;;- : int = 10

Embêtez vos profs d'info, et ne respectez plus jamais aucune indentation...


Bon code !