pamaury a écrit 10 commentaires

  • [^] # Re: Il sers à quoi

    Posté par  . En réponse au journal L'EFF quitte le W3C. Évalué à 6.

    Je ne suis pas expert mais je pense que ce que voulais l'EFF c'est obliger les distributeurs de DRM a mettre dans leur licence que le reverse-engineering est autorisé (ou un truc du genre). En effet aux US c'est illégal par défaut avec le DMCA. Or les grands distributeurs de DRM sont basés aux États-Unis.

  • [^] # Re: Pourquoi du théorie des patch c'est bien

    Posté par  . En réponse au journal Pijul, un nouveau gestionnaire de source. Évalué à 3.

    En effet, je ne dispute pas le fait que de toute façon il faut toujours regarder le résultat d'un merge. Mais je ne pense que pas qu'il faut voir pijul comme un projet qui veut remplacer git. L'idée c'est plutôt d'expérimenter avec un modèle différent.

    D'ailleurs je ne dirais pas qu'il y a pas que c'est futile. En problème typique en git qui n'existe pas en pijul et darcs: je fork master dans une branche X. Je fais plein de commits, de son côté master diverge. Puis je cherry-pick un commit de master très important dans X. Plus tard je merge X dans master: il y a un conflit car X est appliqué deux fois, git ne réalise pas que c'est "le même commit". Je recommande de lire les deux articles suivants qui expliquent bien les différences, cela ne concerne pas que les merge:
    merging et merging and pjiul (notamment "Case study 2: parallel development" pour l'exemple ci-dessus).

  • # Pourquoi du théorie des patch c'est bien

    Posté par  . En réponse au journal Pijul, un nouveau gestionnaire de source. Évalué à 6.

    Je suis le développement de pijul depuis quelques temps et cet article n'évoque pas un (peut-être LE) point essentiel de pijul (alors que le lien vers le blog ne parle que de ça): pijul a un théorie correcte des patch. Pourquoi c'est important ? Avec git, svn, hg, etc (en gros tous sauf pijul ou darcs), on peut faire un merge de deux codes et se retrouver avec un code inattendu sans conflit. Qu'est-ce que l'on entend par inattendu ? Idéalement il faut lire le blog, sinon ce lien donne un explication visuelle. De la façon dont je vois ça, c'est du même ordre que la différence entre un langage dont le système de type est "sound" (O'Caml, Rust dans le fragment sûr par exemple) ou "unsound" (C, C++, Java, la plupart en fait). Certes on peut tout faire en C/C++/Java mais on peut écrire des choses qui n'ont pas de sens ou ne produisent par le résultat escompté, exemple en Java. Bien sûr être sound/unsound ne suffit pas à être un bon ou un mauvais langage et comme la plupart de ces choses, ce n'est souvent pas le candidat le meilleur techniquement qui gagne. Cela dit on peut espérer un jour avec des langages et DCVS qui sont agréables à utiliser ET corrects.

  • [^] # Re: Il sers à quoi

    Posté par  . En réponse au journal L'EFF quitte le W3C. Évalué à 10.

    Justement la position de l'EFF n'est pas celle que tu crois. L'EFF dit "ok on standardise mais on veut que ce soit légal de faire du reverse-engineering" alors que le W3C dit "on standardise et c'est illégal de faire du reverse-engineering".
    La différence est cruciale: si pas de reverse-engineering alors on ne peut pas chercher les bugs de sécurité donc par définition les navigateurs vont embarqué du code non légalement auditable, je trouve que c'est plutôt grave.

  • # C'est une alpha

    Posté par  . En réponse au journal La déception skype. Évalué à 5.

    Si tu lis bien la FAQ (https://support.skype.com/en/faq/FA34656/more-information-about-skype-for-linux-alpha), tu verras qu'il s'agit d'une version alpha et que toutes les fonctionnalités devraient être implantées dans les "semaines qui viennent".

  • # De la question des exploits

    Posté par  . En réponse au journal À propos de la petite bête dans votre ordinateur. Évalué à 9.

    Ces travaux, notamment sur le Management Engine (ME) de Intel, posent pour moi un problème encore plus embêtant: le code du ME est signé par Intel, on peut donc supposer qu'il ne fait pas (trop) de choses (vraiment trop) déplaisantes (encore que). Mais ce code est aussi énorme, il contient une VM Java et permet d'exécuter des modules (http://fr.slideshare.net/codeblue_jp/igor-skochinsky-enpub). Et c'est là que cela devient effrayant: si quelqu'un trouvait un exploit quelconque dans ce code, il pourrait potentiellement faire n'importe quoi sur la machine. On peut même imaginer pire: puisque le ME peut recevoir des commandes à distance (en théorie seul Intel peut le faire car il y a une clef par machine, mais bon, bug toussa), un exploit suffisamment grave pourrait permettre d'exécuter du code à distance sur n'importe quelle machine, sans que le propriétaire s'en aperçoive (même le noyau ne peut pas s'en apercevoir). C'est théorique mais vraiment très effrayant de savoir que ta machine ne peut pas booter sans un code qui contient potentiellement/probablement un backdoor.
    C'est un peu du même ordre que le bug qui permettait de prendre le contrôle de n'importe quelle voiture connectée sur le réseau GSM d'une certaine marque (http://www.wired.com/2015/07/hackers-remotely-kill-jeep-highway/). Sauf que le ME est inclut dans BIOS et pas désactivable, donc il faut reflasher un BIOS, ce qui n'est pas trivial.

  • # Pourquoi les gens ne jurent que par IDA

    Posté par  . En réponse au journal Disséquer du binaire sous linux. Évalué à 10.

    Sur le principe, je suis d'accord avec toi: j'aimerais bien utiliser autre chose qu'IDA, ne serait-ce que pour pouvoir l'améliorer et aussi pour avoir un logiciel libre mais seulement voilà, j'ai passé beaucoup beaucoup de temps avec IDA et aucun autre logiciel que je connais n'est au niveau. J'ai pas testé radare depuis un moment mais à l'époque c'était vraiment pas ça.

    Le problème avec ce genre de logiciel c'est que le main d'oeuvre nécessaire pour atteindre un tel niveau est vraiment importante. Un autre problème pour moi c'est que tous ces logiciels libre ont une mauvaise approche du problème. En gros il te permettent de désassembler, de trouver les "basic block" et voilà. Quant aux graphes d'appels, je n'ai jamais utilisé ça, c'est illisible dès que la fonction n'est pas triviale. Or il me semble que faire ça c'est le niveau 0 du reverse engineering. C'est tout le reste qui est important:

    • l'analyse de la mémoire: savoir quel objet est référencé par qui, sans ça tu ne peux rien faire
    • typage: pouvoir définir des structures, pouvoir "décorer" le code de façon à ce que (exemple en ARM):

      ldr rO, [r0, #40]

      devienne

      ldr r0, [r0, #masuperstructure_t.champ_magique]

      Il faut avouer que c'est plus lisible. Et j'ajouterai que je ne parle pas de le faire à la main, c'est très important que le logiciel puisse comprendre ce genre de décoration (voir plus bas).
      Idéalement il faudrait même aller plus loin: une fois qu'on a indiqué que, par exemple, à un endroits du code, R0 contient un pointeur vers une structure de type masuperstructure_t, il devrait pouvoir automatiquement décorer les autres accès.

    • l'analyse du code: c'est bien beau de désassembler mais en fait ça sert à rien si on a pas d'information sémantiques. Par exemple c'est utile de savoir qu'en ARM l'instruction

      ldr r0, [r0,#40]

      fait un accès à la mémoire à l'adresse R0+40. Et ce n'est pas du tout spécifique à l'ARM, on a ce genre de construction dans tous les assembleurs, plus ou moins simplement. A plus long terme, en y réfléchissant bien, on voit que c'est la seule façon d'essayer de faire de la décompilation. Mais même sans viser aussi loin, un logiciel qui serait capable de prendre le code assembleur, le mouliner et dans 95% des cas le transformer en un code équivalent dans un pseudo-assembleur ou un langage de très bas niveau (style l'assembleur de LLVM) serait vraiment idéal, car après tu peux écrire plein d'algo très génériques d'analyse de code. Évidemment arriver à faire ça est quand même super dur.

    • l'analyse des appels: pouvoir décorer une fonction (avec un prototype en C comme dans IDA ou n'importe quelel autre méthode) de façon à ce que le logiciel soit conscient de la convention d'appel et puisse, lors d'un appel, décorer ou indiquer quels sont les argument. Exemple: on a une fonction "f" et on indique que son type est "void f(int a, int b, int c, int d, int e)", plus tard on voit ce code:

      ldr r0, ... ; a
      ldr r1, ....; b
      ldr r2, ....; c
      ldr r3, ....; d
      str r4, [sp]; e
      blx f

      Là on voit que le fait de savoir quel argument correspond à quoi est utile (en mettant des noms plus lisible que a, b, c, … bien sûr). Et ça marche aussi pour l'analyse de la fonction elle même:

      ; void f(int a, int b, int c, int d, int e)
      f:
      ldr r4, r0; a
      ldr r5, [sp,#4] ; e

      Sauf qu'évidemment dans le cas général ça se complique rapidement: il faut savoir analyser la pile d'appel, ce qui est indécidable dans le cas général mais il existe plein d'algo plus ou moins coûteux/puissant dans la littérature et c'est ça la valeur ajouté du logiciel.

    • l'analyse de la pile: j'en ai parlé au dessus mais ce n'est pas spécifique à un appel de fonction: que le logiciel puisse suivre l'évolution de la pile et comprendre que:

      ldr r0, [sp, #10]

      c'est pas juste un accès tout bête à la mémoire, c'est un accès une variable locale, et comme le logiciel suit l'évolution de la pile il peut même exactement laquelle, lui donner un nom, …

    • être intéractif: je crois que c'est LE point le plus dur mais c'est aussi le I dans IDA :) Le fait de pouvoir regarder le code, décider que paf telle variable que le logiciel a nommé "a" s’appelle en fait "mastructure" et a pour type "masuperstructure_t" et ensuite laisser le logiciel digérer l'info et en tirer les conséquences qui s'impose. Alors évidemment je vais me faire taper par tous les gens qui trouve que les GUI clicou-clicou c'est le Mal(C), c'est Windows-Like(C) ou je sais pas quoi, mais (1) on peut le faire en console avec ncurses (c'est horrible mais pourquoi pas), (2) le logiciel n'a pas accès un Oracle pour nommer et typer les variables, il faut bien bosser à un moment.

    Bref en conclusion je dirais que la raison principale c'est que quand tu as vraiment besoin de faire de la rétroingénierie, la dernière chose dont tu as envie c'est de perdre des heures à faire ce qu'un logiciel peut faire mieux que toi. C'est déjà assez difficile comme ça.
    Peut-être qu'une partie du problème vient aussi du fait que les gens qui font vraiment de la rétroingénierie (et donc savent ce qu'ils veulent) n'ont pas le temps de contribuer à un tel logiciel open source. En tout cas c'est mon cas.

    Si d'autres ont une opinion différente ou peuvent me prouver que je suis médisant et que certains logiciels open source font ça alors je serai heureux d'avoir tord :)

  • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

    Posté par  . En réponse à la dépêche Présentation de Rust 0.8. Évalué à 1.

    Effectivement, je n'aurai pas du utiliser correct: disons une sémantique qui garantie un minimum de sécurité, c'est à dire que la sémantique formelle correspond à l'intuition. Lorsque ce n'est pas le cas (comme en C++ ou autres), on peut écrire du code qui a du sens (probablement), mais pas forcément le sens auquel on s'attend, d'où un bug potentiel.

    Si j'ai bien compris, les "conditions" de Rust, fonctionnent comme une sorte de callback: on définit une condition d'erreur C, et lorsqu'une fonction X rencontre une erreur, elle appelle C en donnant le contexte de l'erreur. Ailleurs dans la pile d'appel de X, une autre fonction va recevoir le message de C (ou bien échouer lamentablement si aucune fonction n'a préciser qu'elle voulait intercepter C) et va pouvoir faire deux choses: exécuter du code (par exemple ajouter un message d'erreur à une liste) et renvoyer une valeur par défaut/sûre/témoins. Le code va retourner à X qui va recevoir de la part C la valeur en question et continuer. La doc est très bien faites donc je ne vais pas répéter l'exemple. Mais informellement, la valeur retournée par C va indiquer à X l'action qu'il doit effectuer: par exemple abandonner le traitement, ou poursuite avec une valeur "sentinelle" (-1), ignore cette tâche (une ligne d'un fichier), etc. Mais le point le plus important, c'est que ce n'est pas X qui va choisir l'action (il va seulement l'appliquer), c'est une fonction quelque part dans la pile d'appel, ce qui permet de gérer les erreurs de façon différentes en fonction du contexte. Cela me paraît très souple lorsque c'est bien utilisé, par contre attention à ne pas générer du code incompréhensible !

  • [^] # Re: De la complexité de la gestion de la mémoire et d'autres

    Posté par  . En réponse à la dépêche Présentation de Rust 0.8. Évalué à 2.

    Exactement, c'est bien le problème de la plupart des langage actuels: héritage "implique" sous-typage, d'où aucune sémantique ou une sémantique contre-intuitive. Si je ne dis pas de bêtise, cela marche en OCaml car sous-typage et héritage sont découplés: le sous-typage est structurel et pas nominatif, l'héritage permet simplement de réutiliser du code.
    Merci pour le commentaire et le lien :p

  • # De la complexité de la gestion de la mémoire et d'autres

    Posté par  . En réponse à la dépêche Présentation de Rust 0.8. Évalué à 10.

    J'ai beaucoup aimé cet article, je trouve qu'il présente bien les fonctionnalité de ce langage qui semble encore plus intéressant à chaque version. Maintenant qu'il commence à se stabiliser, je pense que je vais finir par m'y mettre.

    La gestion de la mémoire semble compliqué au premier abord mais j'aimerais ajouter pour ceux qui pense que c'est une hérésie que c'est aussi la voie vers laquelle le C++ s'oriente avec C++11 et C++14. Pour paraphraser Herb Sutter: "dans le futur, aucun programmeur C++ ne devrait utiliser le type pointeur, mais uniquement utiliser unique_ptr, shared_ptr, weak_ptr, …" (http://herbsutter.com/elements-of-modern-c-style/). Certes le C++ est un langage très (trop ?) compliqué mais pour moi ceci prouve que la thématique de la mémoire est loin d'être réglée. On peut invoquer OCaml ou Java comme "success-story" pour le ramasse miette mais la réalité montre que que ce n'est pas si simple, même si cela marche très bien dans beaucoup de cas. D'où l'intérêt d'avoir une gestion intermédiaire, qui optimise statiquement le plus de cas possible. D'ailleurs, une partie de la recherche actuelle dans les langages de programmation porte sur une gestion encore plus fine des pointeurs (http://protz.github.io/mezzo/). D'ailleurs il me semble que la gestion de la mémoire par Rust n'est pas si éloignée de la logique linéaire, comme cela a été noté plus haut.

    J'aimerais aussi ajouter un point sur la partie "objet": j'ai lu certains commentaires de gens qui trouve le mélange objet/fonctionnel un peu étrange. Je trouve au contraire que le compromis atteint est très bon: les fameuses 15 dernières années de recherche en langages de programmation ont montré que concevoir un langage de programmation objet correct est extrêmement difficile, d'ailleurs la plupart des langage objets n'ont pas une sémantique claire. Il a fallut atteindre 2012 pour que quelqu'un démontre que le C++ avait une sémantique correcte (http://herbsutter.com/elements-of-modern-c-style/) et encore, cette étude a montré que la spécification est vague, voir contradictoire, et dans de nombreux cas complètement contre-intuitive, et elle a même menée à des corrections de la spécification. Ceci semble indiquer que l'approche "C++" ou "Java" des objets n'est pas la bonne: elle semble sympathique mais engendre énormément de problèmes, principalement par son manque de sécurité. L'approche des "traits" me semble plus intéressante: elle conserve la partie intéressante des objets, à savoir l'interface, elle donne la possibilité à un objet d'avoir plusieurs interfaces, et surtout elle dissocie complètement l'objet et l'implémentation de son interface. D'où une question intéressante: est-ce que Rust possède une sémantique correcte ?

    Enfin il est vraiment très appréciable de retrouver de nombreux aspects des langages fonctionnels, comme les lambda fonctions, les types sommes, car ce sont des notions très puissantes que l'on comprend bien. La gestion des erreurs par les "conditions" est aussi intéressante, lorsque utilisée à bon escient.