Académique Documents
Professionnel Documents
Culture Documents
Programmation
Le terme hacker désigne à la fois celui qui écrit du code et celui qui l’exploite.
Même si ces deux groupes de hackers ont des objectifs différents, ils emploient
des techniques similaires pour résoudre les problèmes. Puisque comprendre la
programmation aide ceux qui exploitent et que comprendre les exploits aide ceux
qui programment, de nombreux hackers mènent ces deux activités. Il existe des
astuces intéressantes dans les techniques employées pour écrire du code élégant et
dans celles servant à exploiter des programmes. Le hacking consiste simplement à
trouver une solution astucieuse et non intuitive à un problème.
Les hacks rencontrés dans les exploits se servent généralement des règles de l’ordina-
teur pour contourner la sécurité en empruntant des chemins non balisés. Les hacks
en programmation sont similaires en cela qu’ils emploient également les règles de
l’ordinateur de manière novatrice et inventive, mais l’objectif final recherché est
l’efficacité ou un code source plus petit, pas nécessairement une atteinte à la sécu-
rité. Il est possible d’écrire une infinité de programmes pour accomplir une tâche
donnée, mais la plupart de ces solutions seront inutilement longues, complexes et
mal faites. Les quelques solutions restantes seront petites, efficaces et propres. Les
programmes qui présentent ces qualités sont qualifiés d’élégants, tandis que les solu-
tions astucieuses et novatrices qui conduisent à cette efficacité sont appelées hacks.
Les hackers apprécient la beauté du code élégant et l’ingéniosité des hacks.
Dans le monde professionnel, il est plus important de produire du code opérationnel
en série que de rechercher des hacks et l’élégance. Avec l’augmentation de la puis-
sance des ordinateurs et de la quantité de mémoire disponible, passer cinq heures
supplémentaires à créer un bout de code légèrement plus rapide et plus économe en
mémoire n’a pas vraiment de sens dans l’industrie. Alors que les optimisations
en temps et en mémoire ne sont remarquées que par les utilisateurs les plus sophis-
tiqués, une nouvelle fonctionnalité présente une valeur commerciale. Lorsque
l’objectif est l’argent, passer du temps sur des hacks, uniquement pour une question
d’optimisation, n’a aucun sens.
La véritable appréciation de l’élégance d’un programme est laissée aux hackers :
des mordus de l’informatique dont l’objectif est non pas de gagner de l’argent mais
de tirer le meilleur de chaque bit de leur vieux Commodore 64, des auteurs d’ex-
ploits qui doivent écrire des morceaux de code minuscules et stupéfiants pour se
glisser dans les étroites failles de sécurité, et quiconque apprécie l’idée et le défi de
trouver la meilleure solution possible. Ces personnes sont fortement attirées par la
programmation et apprécient pleinement la beauté d’un morceau de code élégant ou
l’ingéniosité d’un hack. Puisque comprendre la programmation est indispensable à
la compréhension de l’exploitation des programmes, c’est par elle que nous allons
commencer.
Toutes les personnes qui connaissent le français peuvent comprendre et suivre ces
indications, puisqu’elles sont écrites en français. Nous sommes d’accord, elles ne
sont pas éloquentes, mais chaque instruction est claire et facile à comprendre, tout
au moins pour ceux qui lisent le français.
Cependant, la langue maternelle d’un ordinateur n’est pas le français ; il comprend
uniquement le langage machine. Pour expliquer à un ordinateur comment effectuer
une opération, les instructions doivent lui être données dans sa propre langue. Cepen-
dant, un langage machine est mystérieux et il est difficile de travailler avec – il est
constitué de bits et d’octets et diffère d’une architecture à l’autre. Pour écrire un
programme en langage machine pour un processeur Intel x86, vous devez déterminer
la valeur associée à chaque instruction, l’effet de chaque instruction et une myriade
de détails de bas niveau. Ce type de programmation demande beaucoup de soin et
reste lourde. Elle n’est en aucun cas intuitive.
Pour contourner la complexité du langage machine, nous avons besoin d’un traduc-
teur. Un assembleur est une forme de traducteur de langage machine – il s’agit d’un
programme qui convertit le langage assembleur en code lisible par la machine. Le
langage assembleur est moins énigmatique que le langage machine, car il utilise des
noms pour les différentes instructions et variables, à la place des valeurs. Cependant,
il est encore loin d’être intuitif. Les noms des instructions sont très ésotériques et
le langage est propre à chaque architecture. Tout comme un langage machine pour
les processeurs Intel x86 est différent d’un langage machine pour les processeurs
Sparc, un langage assembleur x86 diffère de son homologue pour Sparc. Chaque
0x220 Pseudo-code
Les programmeurs disposent d’une autre forme de langage de programmation,
appelée pseudo-code. Un pseudo-code n’est rien d’autre que du français intégré
à une structure semblable à un langage de haut niveau. Il n’est pas compris par
les compilateurs, les assembleurs ou les ordinateurs, mais il permet au program-
meur d’organiser ses instructions. Le pseudo-code n’est pas défini avec précision.
En réalité, la plupart des gens l’écrivent de manière légèrement différente. Il s’agit
d’une forme de chaînon manquant entre le français et les langages de programmation
de haut niveau, comme C. Le pseudo-code est parfaitement adapté à la présentation
des concepts de programmation universels.
0x231 If-Then-Else
Dans notre exemple d’indications de direction, Main Street peut être en travaux.
Une suite d’instructions particulières prend en charge ce cas. Sinon les instructions
normales doivent être suivies. Ces cas particuliers sont traités dans un programme
avec l’une des structures de contrôle les plus naturelles, la structure if-then-else
(si-alors-sinon). Elle prend la forme générale suivante :
If (condition) then
{
Instructions à exécuter lorsque la condition est satisfaite;
}
Else
{
Instructions à exécuter lorsque la condition n’est pas satisfaite;
}
Chaque instruction se trouve sur sa propre ligne et les différents blocs d’instruc-
tions conditionnelles sont placés entre des accolades et indentés afin d’en faciliter
la lecture. Dans le langage de programmation C, ainsi que d’autres, le mot clé then
est implicite et donc omis, ce que nous avons fait dans le pseudo-code précédent.
La syntaxe d’autres langages de programmation impose la présence du mot clé
then, par exemple BASIC, Fortran ou Pascal. Ces différences syntaxiques dans les
langages de programmation ne sont que superficielles ; la structure sous-jacente
reste identique. Lorsqu’un programmeur comprend les concepts convoyés par ces
langages, l’apprentissage des variantes syntaxiques pose peu de difficultés. Puisque
C sera utilisé dans les sections ultérieures, le pseudo-code donné dans cet ouvrage
respectera une syntaxe de type C. Cependant, n’oubliez pas que le pseudo-code peut
prendre de nombreuses formes.
En C, lorsqu’un bloc d’instructions entourées d’accolades n’est constitué que d’une
seule instruction, ces accolades sont facultatives. Pour des raisons de facilité de
Cette règle concernant les blocs d’instructions est valide pour toutes les structures
de contrôle mentionnées dans cet ouvrage. La règle elle-même peut être décrite par
du pseudo-code :
If (le bloc d’instructions n’en contient qu’une seule)
Les accolades pour regrouper les instructions sont facultatives;
Else
{
Les accolades sont obligatoires;
Puisqu’un regroupement logique de ces instructions est nécessaire;
}
La description d’une syntaxe peut être elle-même vue comme un programme simple.
Il existe plusieurs variantes de la structure if-then-else, comme les instructions
select/case, mais la logique de base ne change pas : si (if) cela se produit, effec-
tuer ces opérations, sinon (else) effectuer ces autres opérations (qui peuvent inclure
d’autres structures if-then-else).
Les deux lignes qui suivent l’instruction while sont répétées tant que la souris est
affamée. La nourriture trouvée par la souris à chaque tour de boucle peut aller de
Toute boucle de type until peut logiquement être convertie en une boucle while. Les
indications de direction précédentes contenaient l’instruction Continuez sur Main
Street jusqu’à voir une église sur votre droite. Elle peut facilement être convertie en
une boucle while standard en inversant la condition :
While (il n’y a pas d’église sur votre droite)
Continuez sur Main Street;
En réalité, une boucle for n’est rien d’autre qu’une boucle while avec un compteur.
Voici la même indication écrite différemment :
Mettre le compteur à 0;
While (le compteur est inférieur à 5)
{
Continuez votre route pendant 1 kilomètre;
Ajouter 1 au compteur;
}
Avec un pseudo-code de type C pour la boucle for, cela devient encore plus évident :
For (i=0; i<5; i++)
Continuez votre route pendant 1 kilomètre;
0x241 Variables
Le compteur utilisé dans la boucle for est en réalité un type de variable. Une variable
peut simplement être vue comme un objet qui contient des données pouvant varier
(d’où le nom). Il existe également des variables qui ne changent pas et sont donc
appelées constantes. Pour reprendre notre exemple d’indications de direction, la
vitesse de la voiture pourrait être une variable, tandis que sa couleur serait une
constante. Dans le pseudo-code, les variables sont des concepts abstraits, mais
dans certains langages de programmation, comme C, elles doivent être déclarées
et doivent avoir un type avant de pouvoir être utilisées. En effet, le programme C
devra être compilé en un programme exécutable. Tout comme une recette de cuisine
donne la liste des ingrédients avant les instructions, les déclarations de variables
vous permettent d’effectuer des préparations avant d’entrer au cœur du programme.
Toutes les variables sont stockées quelque part en mémoire et leurs déclarations
permettent au compilateur d’organiser plus efficacement ce stockage. Cependant,
une fois cela terminé et malgré les déclarations de type des variables, tout n’est
que mémoire.
En C, chaque variable se voit attribuer un type qui décrit les informations qu’elle
peut contenir. Parmi les types les plus courants, on trouve int (nombre entier), float
(nombre à virgule flottante) et char (un seul caractère). Les variables sont déclarées
en plaçant simplement ces mots clés avant leurs noms.
int a, b;
float k;
char z;
Les variables a et b sont à présent définies comme des entiers, k accepte des nombres
à virgule flottante, comme 3.14, et z est supposée contenir un caractère, comme A
ou w. Pour affecter des valeurs à des variables, au moment de leur déclaration ou
par la suite, on utilise l’opérateur =.
int a = 13, b;
float k;
char z = ’A’;
k = 3.14;
z = ’w’;
b = a + 5;
Après avoir exécuté ces instructions, la variable a contiendra la valeur 13, k contiendra
le nombre 3.14, z contiendra le caractère w et b contiendra la valeur 18, puisque
13 plus 5 est égal à 18. Les variables représentent simplement un moyen de mémo-
riser des valeurs ; cependant, en C, vous devez commencer par déclarer le type de
chaque variable.
Soustraction - b = a - 5
Multiplication * b = a * 5
Division / b = a / 5
Modulo % b = a % 5
Les quatre premières opérations doivent vous être familières. Le calcul du modulo
pourrait vous sembler nouveau, mais il s’agit simplement de prendre le reste d’une
division. Si a vaut 13, alors, 13 divisé par 5 est égal à 2, avec un reste de 3. Autrement
dit, a % 5 = 3. Par ailleurs, puisque les variables a et b sont des entiers, l’instruc-
tion b = a / 5 affecte à la variable b la valeur 2, qui correspond à la partie entière.
Des variables à virgule flottante doivent être utilisées pour mémoriser la réponse
correcte 2,6.
Pour qu’un programme utilise ces concepts, vous devez parler sa langue. Le langage
C propose également plusieurs formes raccourcies pour ces opérations arithmé-
tiques. Nous en avons déjà mentionné une précédemment et elle est communément
employée dans les boucles.
Puisque l’ordre a changé, b contiendra à présent la valeur 36, tandis que a restera à 6.
Dans les programmes, il est assez fréquent que des variables doivent être modifiées
en place. Par exemple, vous pourriez avoir besoin d’ajouter une valeur quelconque,
comme 12, à une variable et stocker le résultat directement dans cette même variable,
comme i = i + 12. Ce type d’opération est tellement fréquent qu’il en existe un
raccourci.
Égal à == (a == b)
Différent de != (a != b)
La plupart de ces opérateurs ont une signification évidente. Cependant, vous noterez
que la version abrégée de égal à utilise deux signes "égal". Ce point est important
car le double signe égal est utilisé pour le test d’équivalence, tandis que le signe égal
seul sert à attribuer une valeur à une variable. L’instruction a = 7 signifie Placer la
valeur 7 dans la variable a, tandis que a == 7 signifie Vérifier si la variable a est
égale à 7. Certains langages de programmation, comme Pascal, utilisent := pour
l’affectation des variables et éliminer ainsi le risque de confusion visuelle. Vous
noterez également qu’un point d’exclamation signifie généralement non. Ce symbole
peut être employé pour inverser le sens d’une expression.
!(a < b) est équivalent à (a >= b)
L’instruction constituée des deux conditions "inférieur à" réunies par un OU logique
donnera la valeur vrai si a est inférieur à b OU si a est inférieur à c. De même, la
partie constituée des deux conditions "inférieur à" réunies par un ET logique aura
la valeur vrai si a est inférieur à b et si a n’est pas inférieur à c. Ces instructions
doivent être placées entre parenthèses et peuvent contenir autant de combinaisons
différentes que nécessaire.
De nombreuses opérations peuvent se réduire à des variables, des opérateurs de
comparaison et des structures de contrôle. Pour revenir à l’exemple de la souris qui
cherche sa nourriture, être affamé peut se traduire en une variable booléenne vrai/
faux. Naturellement, 1 signifie vrai et 0 signifie faux.
While (affamée == 1)
{
Cherche de la nourriture;
Mange la nourriture;
}
Voici un autre raccourci que les programmeurs et les hackers emploient assez
souvent. Le langage C n’a pas réellement d’opérateurs booléens. Toute valeur diffé-
rente de zéro est donc considérée comme égale à vrai et une instruction vaut faux
si elle contient 0. En réalité, les opérateurs de comparaison retournent la valeur 1
lorsque la comparaison est vraie et 0 lorsqu’elle est fausse. En testant si la variable
affamée est égale à 1, nous obtenons la valeur 1 si affamée est égale à 1 et la valeur 0
si elle est égale à 0. Puisque le programme ne vérifie que ces deux cas, l’opérateur
de comparaison peut être omis.
While (affamée)
{
Cherche de la nourriture;
Mange la nourriture;
}
Dans le programme de souris plus intelligente suivant, nous utilisons des entrées
supplémentaires pour illustrer la combinaison des opérateurs de comparaison et des
variables :
While ((affamée) && !(chat_présent))
{
Cherche de la nourriture;
If (!(nourriture_est_dans_souricière))
Mange la nourriture;
}
Cet exemple suppose que des variables décrivent la présence d’un chat et l’emplace-
ment de la nourriture, avec la valeur 1 pour vrai et 0 pour faux. N’oubliez pas que
toute valeur différente de zéro signifie vrai et que 0 signifie faux.
0x244 Fonctions
Parfois, le programmeur saura qu’il doit utiliser plusieurs fois le même bloc d’ins-
tructions. Ces instructions peuvent être regroupées dans un petit sous-programme
appelé fonction. Dans certains langages, les fonctions sont appelées sous-routines ou
procédures. Par exemple, l’action de changer de direction en voiture est composée
de plusieurs instructions plus petites : mettre les clignotants du bon côté, ralentir,
vérifier si des véhicules arrivent en sens inverse, tourner le volant dans la direc-
tion appropriée, etc. Les indications de chemin données au début de ce chapitre
impliquent plusieurs changements de direction. Cependant, donner la liste de chaque
petite instruction séparée pour chaque changement de direction serait vite fastidieux
(et moins lisible). Vous pouvez passer des variables en argument à une fonction afin
d’en modifier le comportement. Dans notre cas, la fonction prend un paramètre qui
indique le sens du changement de direction :
Function Tourner(variable_direction)
{
Mettre les clignotants à variable_direction;
Ralentir;
Vérifier si des véhicules arrivent en sens inverse;
while (des véhicules arrivent en sens inverse)
{
Attendre;
Vérifier si des véhicules arrivent en sens inverse;
}
Tourner le volant vers la variable_direction;
while (le virage n’est pas terminé)
{
if (vitesse < 5 km/h)
Accélérer;
}
Tourner le volant vers sa position d’origine;
Éteindre les clignotants;
}
Cette fonction est déclarée comme un entier car elle multiplie entre elles toutes les
valeurs dans l’intervalle 1 à x et retourne le résultat, qui est un entier. L’instruction
return placée à la fin de la fonction retourne le contenu de la variable x et termine la
fonction. Cette fonction factorielle() peut être utilisée comme une variable entière
dans la partie principale de tout programme qui la connaît.
int a=5, b;
b = factorielle(a);
fonction de changement de direction plus complète, qui est écrite dans un pseudo-
code de type C :
void tourner(variable_direction, nom_rue_cible)
{
Rechercher une plaque de rue;
nom_intersection_actuelle = lire le nom sur la plaque de rue;
while(nom_intersection_actuelle != nom_rue_cible)
{
Rechercher une autre plaque de rue;
nom_intersection_actuelle = lire le nom sur la plaque de rue;
}
Mettre les clignotants à variable_direction;
Ralentir;
Vérifier si des véhicules arrivent en sens inverse;
while (des véhicules arrivent en sens inverse)
{
Attendre;
Vérifier si des véhicules arrivent en sens inverse;
}
Tourner le volant vers la variable_direction;
while (le virage n’est pas terminé)
{
if (vitesse < 5 km/h)
Accélérer;
}
Tourner le volant vers sa position d’origine;
Éteindre les clignotants;
}
Une partie de cette fonction tente de trouver la bonne intersection en cherchant les
plaques de rues, en lisant le nom inscrit sur chacune et en enregistrant ce nom dans
la variable nom_intersection_actuelle. Elle cherche et lit les plaques jusqu’à ce
que la bonne rue soit trouvée. À ce moment-là, les instructions de changement de
direction sont exécutées. Le pseudo-code des indications de direction peut mainte-
nant être modifié de manière à utiliser notre nouvelle fonction.
Sur Main Street, allez vers l’est;
While (il n’y a pas d’église sur votre droite)
Continuez sur Main Street;
If (la rue est bloquée)
{
Tourner(droite, 15e rue);
Tourner(gauche, Pine Street);
Tourner(droite, 16e rue);
}
Else
Tourner(droite, 16e rue);
Tourner(gauche, Destination Road);
For (i=0; i<5; i++)
Continuez votre route pendant 1 kilomètre;
Arrêtez-vous au 743 Destination Road;