Ce site n'est pas le site officiel baboon.rce.free.fr.
C'est un blog automatisé qui réplique les articles de baboon.rce.free.fr
Salut les zouaves, je sais que j'avais dit que je ne posterai plus mais il m'est arrivé 3 trucs pas cools ces derniers temps et j'ai besoin de vous pour réparer un peu tout ca ...
) et donc avec lui tout mes codes, jusque là ca peut encore aller, j'ai des backup pas trop vieilles chiffrées sur mon ftp et c'est là qu'intervient le 3ème fail.Bref j'ai perdu quasiment la totalité de mes codes (aussi et surtout ceux que je n'avais pas publiés ...) donc si une personne ici a téléchargé certains de mes codes et qu'il me les envoi à l'adresse suivante : baboon [ POINT] rce [ AT ] gmail [ POINT ] com ca serait cool.
En échange eh bien je lui paierai une bière à la première occase et je lui dédicacerai une photo de mes fesses (2ème lot optionnel).
[EDIT] ah oui et tient, histoire de pas poster pour rien, j'ai recodé la partie échange de clef Diffie-Hellman de fish (le plugin xchat permettant le chiffrement des communications) avec openssl, n'arrivant pas à le faire marcher avec miracl sous mon windows 7 64 bits donc voilà le code et le binaire (pour compiler, modifiez le .bat avec votre path openSSL), c'est crade, j'avais pas prévu de le release mais au moins ca marche : ICI
[RE-EDIT] bon, bonne nouvelle, j'ai fini par récupérer ma clé USB, perdue avec mon kit de lockpick dans la bagnole d'un pote. J'ai donc pu déchiffrer mes backup et j'ai récupéré la totalité de mes codes (enfin ceux fait avant Juin 2011). je remercie vivement les quelques personnes qui m'ont contacté pour m'envoyer mes codes ou pour me soutenir moralement (`Ivan, va mourir). Je laisse ce post pour la postérité, si un jour vous êtes déprimé, si un jour vous pensez que votre vie ressemble à du VB6 émulé par du windev exécuté sur du sparc 64bit, lisez ces quelques lignes et rappelez vous qu'il y a toujours quelqu'un qui fail plus que vous et pour qui (parfois) ca fini (presque) bien quand même :D
Source: http://baboon.rce.free.fr/index.php?post/2011/10/31/Qui-qu-a-mes-codes
Salut les zouaves, comme le titre de ce post l'indique, ce blog va fermer ses portes.
Je n'arrête pas le RE, je ne pars pas élever des chèvres (ni de poules) dans le Lubéron, je change juste de peau pour repartir plus blanc que blanc (lavé avec OMO).
Logiquement tous devrait rester en place sauf si un script kiddy s'amuse à defacer ma page avec un goatse 
Je devrai normalement rester sur IRC si vous voulez me faire un coucou sur #fat ou #uct (irc.epiknet.org).
Pas de mercis ou d'adieu donc, je ne suis pas mort !
Source: http://baboon.rce.free.fr/index.php?post/2011/03/24/Fermeture-du-blog
Un petit post rapide pour partager avec vous un petit script fait à la va vite en python mais dont le résultat est, en toute modestie, pas dégueu.
J'ai récemment résolu une petite série de crackme linux pas bien méchants mais dont l'un comprenait une quantité non négligeable de code mort. Pour le résoudre sous IDA cela prenait 2 secondes, il suffisait de regarder les blocks sautant sur le printf "bad boy" pour retrouver les 2 uniques tests faits sur le sérial. Néanmoins les conditions "normales" du challenges exigeaient de ne se servir que des outils standards et gratuits des barbus qui puent.
J'ai donc décider de coder rapidement un petit outil personnalisable permettant de transformer la sortie de objdump en quelque chose d'humainement lisible, dans le même esprit que les graphs fournit par IDA.
Pour ce faire j'ai codé un petit script python à base de regexp qui prend en paramètre la sortie de objdump et vous donne en retour un fichier .dot compilable à l'aide de graphviz.
et voila le résultat :
Le script python n'est pas très long, je vous le paste donc ici :
''' objdump --no-show-raw-insn -d -M intel > log idagraph log rules dot -Tsvg -Ln20 -LC2 -omain.svg *.dot ''' import sys import re def decorate(line): color = "" font = "" for display_rule in display_rules : if display_rule[0].match(line) : color += " "+display_rule[1] for font_rule in font_rules : if font_rule[0].match(line) : font += " "+font_rule[1] return "<TR><TD "+color+">"+(font and ("<FONT %s >"%font) or "" )+line.replace("<","").replace(">","")+(font and "</FONT>" or "" )+"</TD></TR>" rules = {"functions":[],"graphparam":[],"table_param":[],"font_rule":[],"display_rule":[],"label_font":[],"label_display":[]} nodes = {} labels = {} currnode = 0 funregex = re.compile("(?P<address>[0-9a-f]+)[\t ]+<(?P<funname>[^>]+)>:") jumpregex = re.compile(" (?P<address>[0-9a-f]+):[\t ]+(?P<jmptype>j[^ ]+)[\t ]+(?P<destadd>[0-9a-f]*)([\t ]+<(?P<destid>[^>]+)>)?") disasregex = re.compile(" ?(?P<address>[0-9a-f]+):?[\t ]+(?P<disasm>.*)") parseit = False if len(sys.argv) < 2 : print "pyda.py objdumplog [rules]" sys.exit(0) log = file(sys.argv[1], "rb") print "parsing rules ..." if len(sys.argv) > 2 : frules = file(sys.argv[2],"r") raw_rules = frules.readlines() for raw_rule in raw_rules : print [rule.strip() for rule in raw_rule.strip().split("\t",1)] type, rule = [rule.strip() for rule in raw_rule.strip().split("\t",1)] if not type in rules : raise Exception("invalid rule : %s"%raw_rule) else : rules[type].append(rule) print "parsing rules : OK" display_rules = [] print "parsing display rules ..." for raw_display_rule in rules["display_rule"] : regex , display_rule = [rule.strip() for rule in raw_display_rule.split("\t",1)] try : display_rules.append((re.compile(regex), display_rule)) except : raise Exception("invalid color rule : %s"%raw_display_rule) print "parsing display rules : OK" font_rules = [] print "parsing font rules ..." for raw_font_rule in rules["font_rule"] : regex , font_rule = [rule.strip() for rule in raw_font_rule.split("\t",1)] try : font_rules.append((re.compile(regex), font_rule)) except : raise Exception("invalid color rule : %s"%raw_font_rule) print "parsing font rules : OK" print "parsing log ..." while (True): d = log.readline() if not d : break disas = disasregex.match(d) if not disas : continue f = funregex.match(d) if f : if not rules["functions"] : yn = None while not (yn in ["Y","N"]): print "include %s:%s in graph ? (y|n)"%(f.group("address"),f.group("funname")) yn = sys.stdin.readline().strip().upper() if yn == "Y" : parseit = True elif yn == "N" : parseit = False else : parseit = f.group("funname") in rules["functions"] print "parse %s:%s :"%(f.group("address"),f.group("funname")),f.group("funname") in rules["functions"] if parseit : currnode = f.group("address") nodes[currnode] = (f.group("funname"), [], [], {}, {}) labels[f.group("address")] = "fun_"+f.group("funname").replace("<","").replace("<","").replace(" ","_") continue if not parseit : continue j = jumpregex.match(d) if j : nodes[currnode][3][j.group("address")] = (j.group("jmptype"),j.group("destadd")) nodes[currnode][4][(j.group("destadd"))] = None if j.group("destid") : labels[j.group("destadd")] = "loc_"+j.group("destid").replace("<","").replace("<","").replace(" ","_") nodes[currnode][1].append(disas.group("disasm")) nodes[currnode][2].append(disas.group("address")) print "parsing log : OK" for fun in nodes : filename = (nodes[fun][0] and nodes[fun][0] or fun) +".dot" print "generating %s ..."%filename try : fdot = file(filename, "w") fdot.write("digraph G {\n") for i in rules["graphparam"] : fdot.write(i+"\n") name, lines, addresses, jmps, destinations = nodes[fun] currname = "_"+fun blocks = {currname:[]} relations = [] for i in xrange(len(lines)) : lines[i] = decorate(lines[i]) i = 0 while i < len(addresses) : if addresses[i] in jmps : blocks[currname].append(lines[i]) jmptype, destaddr = jmps[addresses[i]] if i+1 < len(addresses) : nextname = "_"+addresses[i+1] else : nextname = None destname = "_"+destaddr relations.append((currname, destname, (jmptype == "jmp") and "blue" or "green", (jmptype == "jmp") and "s" or "se")) if nextname and jmptype != "jmp" : relations.append((currname, nextname, "red", "sw")) currname = nextname if currname : blocks[currname] = [lines[i+1]] i+=1 elif addresses[i] in destinations : if i != 0 and not addresses[i-1] in jmps : relations.append((currname, "_"+addresses[i],"blue", "s")) currname = "_"+addresses[i] blocks[currname] = [lines[i]] else : blocks[currname].append(lines[i]) i+=1 for relation in relations : fdot.write(relation[0]+" -> "+relation[1]+" [color=%s, tailport=%s];\n"%(relation[2],relation[3])) for block in blocks : fdot.write(block+" [label=<<TABLE %s>%s</TABLE>>];\n"%(rules["table_param"] and rules["table_param"][0] or "", decorate(labels.get(block[1:],"loc_"+block[1:]).replace("<","").replace(">","")) + "".join(blocks[block]))) fdot.write("}\n") fdot.close() print "generating %s : OK"%filename except : print "generating %s : FAIL"%filename
le script prend en paramètre la sortie de objdump ainsi qu'un fichier de règle optionnel (mais pas vraiment, la sortie sans ce fichier est dégueulasse)
Le fichier de règle est très simple et pour les différentes options qui sont utilisables, je vous conseille de vous référer au manuel graphviz, Grâce à cet aide ainsi qu'au fichier de règle d'exemple suivant (utilisé pour produire l'image plus bas) vous ne devriez pas avoir de mal à faire votre propre rendu. (atention les séparateurs doivent être des tabulations)
functions main graphparam graph [splines=true]; graphparam edge [color=blue, arrowsize=2]; graphparam node [color=lightblue, style=filled, shape=box, fontname="Verdana"]; table_param ALIGN="LEFT" BGCOLOR="lightblue" BORDER="0" CELLBORDER="0" display_rule call.* BGCOLOR="blue" display_rule jmp BGCOLOR="yellow" display_rule j[^m] BGCOLOR="yellow" display_rule .* ALIGN="LEFT" font_rule j[^m] COLOR="red" font_rule loc_ COLOR="darkblue" FACE="Georgia" POINT-SIZE="20" font_rule fun_ COLOR="darkblue" FACE="Georgia" POINT-SIZE="20"
Un exemple un peu plus complexe (le crackme en question) pour vous montrer ce qu'est capable de produire pyda (un peu prétentieux comme nom mais j'assume complètement) et graphviz.
Attention opera chie dans la colle avec ce svg, préférez firefox ...).
Bien sur ca n'atteint pas le niveau de IDA mais ca permet de dépanner les pauvres barbus sans le sous 
Source: http://baboon.rce.free.fr/index.php?post/2010/10/14/Pourquoi-le-libre-c-est-nul-sauf-le-python
Salut les zouaves, aujourd'hui on ne rigole plus, c'est du lourd ! (et je ne parle pas de mon pwnie award ! (hop comme ça je me la pète en même temps))
Mon stage s'étant terminé (depuis un moment), j'ai pu reprendre l'analyse de armadillo, hélas, une fois tout décortiqué et malgré quelques petites choses intéressantes, je n'avais pas grand chose pour faire un post digne de ce nom et était donc passé à autre chose.
Heureusement pour vous, un hasard de calendrier me permet aujourd'hui de vous fournir quelque chose de croustillant à savoir : Pourquoi Armadillo a été keygenné (par NGEN).
Cette information est passée plutôt inaperçue, aucune info sur google, aucun post sur un forum de reverse (enfin à ma connaissance) et je serai probablement passé à coté si une petite souris ne m'avait pas subrepticement susurré le lien donné précédemment.
Avant toute chose, petit retour en arrière dans le temps, afin de bien vous expliquer la faille dans l'algorithme de vérification d'armadillo il est nécessaire de le comprendre un minimum, je reprends donc mon ancien post là où il en été.
Grâce à la piste fournie par jB, turbodiff, godup et tuts4you j'ai fini (ou c'est tout comme) l'analyse de la vérification de clef par armadillo ainsi que leurs génération.
Je maintiens néanmoins que cette partie (et pas le reste de la protection Mr Papa d'Armadillo
) est vraiment codée avec les pieds et mériterait des coups de genoux biens placés dans les roubignolles (ce qui se confirmera par la suite :p ).
Tout d'abord un petit récapitulatif des outils et "techniques" utilisées pour étudier ce code horrible.
J'ai tout d'abord dumpé la DLL utilisée par armadillo pour vérifier le sérial, elle n'est pas loadée à l'aide de LoadLibrary et donc non directement dumpable à l'aide de LordPE mais cela se fait bien quand même.
Après un passage sous IDA, les fonctions standards C sont automatiquement reconnues, ce qui commence déjà à faciliter le boulot. J'avais commencé mon analyse avec juste ces informations et commencé à renommer toutes les fonctions manipulant des bignums, il ne me manquait que les grosses fonctions utilisées pour manipuler les points d'une courbe elliptique.
Là j'ai utilisé les sources fournies par jB qui ne correspondent pas exactement au code d'armadillo mais qui ont l'avantage de présenter la même structure (la multiplication d'un point, sa génération, l'algorithme DSA, etc. sont composés de blocs chainés de façon particulière) j'ai donc utilisé turbodiff sur la dll ainsi que les .o des fichiers du dossier fast_onb et ai pu identifier les fonctions les plus complexes.
Enfin j'ai exporté le .map et l'ai modifié avec un petit script python afin de rajouter un delta de 0x1000 (godup utilisant le début de la page courant pour créer les labels et la dll étant loadé d'un bloc dans la mémoire, il fallait ajouter la taille du PE à l'adresse des labels) enfin je l'ai chargé à l'aide de godup dans ollydbg ce qui a permis une analyse en duo olly / IDA relativement confortable.
Passons maintenant à la vérification des clefs à proprement parler.
La vérification des clefs de armadillo s'articule autour de plusieurs étapes :
Je vais maintenant détailler les différentes étapes :
Les paramètres publics de la signature ECDSA sont encodés sous la forme d'une string de 3 entiers décimaux séparés par des virgules "A,B,C" A correspond au DWORD g permettant de générer le point de base G sur la courbe elliptique et B et C sont respectivement l'abscisse et l'ordonnée du point public Q (pour plus de détails : wikipédia).
La génération du point est simple, un point preG est généré aléatoirement puis doublé pour donner G, un petit détail qui me turlupine est que le doublement assure juste que l'ordre de G n'est pas divisible par 2 (dans le cas contraire on aurait preG d'ordre 2*2*X divisant 2*n or n est premier (et égal à 5192296858534827627896703833467507)).
Ce qui m'amène à me demander ce qui se passerait si le point preG était d'ordre 2 (probabilité TREEEEEES faible, il n'y a qu'un point d'ordre 2 si je ne me goure pas, mais bon, pas nulle) et donc que l'on se retrouve avec le point à l'infini après doublement ... mais je divague peut être, il y a peut être une vérification à ce niveau que j'ai manqué.
Le décodage du sérial n'échappe pas à la règle et utilise des algorithmes lourds et complètement inutiles (division, multiplication, addition de bignums) pour découper le serial encodé en base 32 (0123456789ABCDEFGHJKMNPQRTUVWXYZ), les tirets sont ignorés, le serial doit commencer par le caractère 1 qui est ignoré (et qui sert, si on se réfère aux sources du keygen armadillo que j'ai fini par trouver à la toute fin de mon analyse ( :'( ) à différencier les différents niveaux de sécurité du serial).
Ainsi, le serial formé à l'aide du script suivant :
>>> def encode(i) : ... s = "" ... base = "0123456789ABCDEFGHJKMNPQRTUVWXYZ" ... while i : ... s = base[i%32] + s ... i /= 32 ... return "1"+s ... >>> encode(0xC0CAC01ADEADBEEF111213141516171819101A1B1C1D010203040506070809000A0B0C0D) '160TB01NQNDQVQH24GK2GAHC5RR3481M6RW3M0G40R40M30E2090050P30D'
Sera décodé comme suit :
Rien de bien compliqué donc, sauf si l'on considère que pour faire ce découpage, notre cher ami armadillo utilise opérations de oufzor sur des bignums pour faire ça, il va vraiment falloir me dire qui a codé ça ....
La vérification de la signature est des plus classique, le nom est concaténé au tableau de byte C(bytes ExtraBytes||K) puis hashé à l'aide de MD5 ce qui nous donne un bigint qui est ensuite utilisé pour la vérification de la signature.
Et... C'est là que le bât blesse...
Les checks de base ne sont pas fait par armadillo sur S1 et S2, à savoir vérifier si ils sont non nuls ou pas, entrez donc 160TB01NQNDQVQG00000000000000000000000000000000000000000000 comme serial et vous allez avoir une surprise
armadillo crash lamentablement !
Après une rapide étude on se rend compte que la routine responsable de ce crash est celle chargée d'inverser le paramètre y de la signature (forcément essayer d'inverser 0 ca le fait pas), on pourrait se dire à ce moment là que la faille est certes amusante et révélatrice de la qualité du code mais qu'elle ne nous avance pas à grand chose... MAIS ! (suspens ...) 0 n'est pas le seul élément nul possible, en effet y = 5192296858534827627896703833467507 est aussi nul modulo ... ba 5192296858534827627896703833467507 ...
Seulement l'inversion de y ne fait pas crasher armadillo, la fonction d'inversion se contente de renvoyer un code d'erreur qui est bien évidemment ignoré par armadillo ... Nous avons alors y^-1 nul par conséquent l'équation (i,j) = [H(m)*y^-1)]G + [x*y^-1)]Q se simplifie en ... (i,j) = [0]G + [0]Q et donc (i,j) = 0 point à l'infini de la courbe et ce quelque soit x et m, ce qui est fort embêtant étant donné que la signature est considérée valide si x = i.
Pour produire une signature valide pour n'importe quel message, il suffit donc de fournir le couple (0.abs, n) à armadillo. Comme je suis gentil je vous fourni même la valeur de ce 0.abs : 0x192B24A1DC800400000000000000.
Le code permettant de générer un serial passant la première vérification de armadillo est disponible ci dessous :
>>> infinite_point_abs = 0x192B24A1DC800400000000000000 >>> n = 0x73EA6DAF91BFFDFFFFFFFFFFFFFF >>> misc = 0xDEADBEEFC0CAC01A >>> encode((misc << 28*8) | (n << 14*8) | (infinite_point_abs)) '16YNPZEZG6AR0D77UKDNY8VZZFZZZZZZZZZZWCJP951VJ00800000000000'
C'est quand même con de tenter de coder des algorithmes cryptographiques puissants sans savoir coder et en ne sachant même pas lire un article sur wikipedia (ou tout simplement les documents très complets du NIST ou autre) ... Après ca fini en drame et la protection se fait trouer (assez tardivement, je l'avoue, ce qui est assez bizarre étant donné que la faille n'est pas récente (elle existe dans la version 6 d'armadillo et on en est à la version 7))
Petite précision avant de passer à la suite, il semblerait que la dernière version de armadillo, sortie avant les keygens d'armadillo, soit """résistante""" à cette faille, elle crash même en utilisant y = n, pour le vérifier, utilisez simplement le "serial" fourni plus haut.
Le déchiffrement de la clef et des EtraBytes est simplement fait à l'aide d'un xor effectué entre le chiffré et la sortie du générateur pseudo aléatoire de Robert Machin initialisé à l'aide du crc32 du nom (traité auparavant à l'aide d'une fonction de standardisation (suppression des caractères blancs et mise en majuscule)).
La vérification de la clef privée K n'est pas très intéressante, sachez juste qu'elle est xorée avec l'HWID si il existe et étendue à l'aide du prng de Roro passer d'une taille de 4 bytes à 0x400, le md5 de cette clef étendue est utilisé comme une deuxième clef et armadillo tente de l'utiliser pour déchiffrer une série de blocs (à l'aide de plusieurs itérations de 2 variantes de TEA, une en CBC et une en EBC), un des bloc une fois déchiffré devant avoir son dernier DWORD égal au not de l'avant dernier, ce bloc déchiffré servant après à faire plein de truc que je n'ai pas regardé...
Je pensais parler plus longuement de cette vérification mais je commence à fatiguer. J'ai codé un bruteforce pour tenter de retrouver cette clef privée, avec mon processeur il me faudrait 200 jours, j'ai donc recodé mon bruteforcer en CUDA, je gagne un petit facteur (de l'ordre de 3-4) ma carte graphique n'étant pas super puissante et en ayant besoin pour mater du pr0n (roooh non je rigole moman, l'auto-collant sur mon laptop n'est pas ce que tu crois) j'ai abandonné mes tentatives de bruteforce (et puis de toute façon c'est pas rigolo).
Voila ! J'espère que ca vous a plus 
Un grand bravo aux gars de NGEN qui ont trouvé cette faille qui trainait depuis un moment et un grand "shame on you" au zouave qui a codé cette partie de arma.
Source: http://baboon.rce.free.fr/index.php?post/2010/09/04/Armadillo-mange-des-ours-en-slips
Mdlolkikoodr tout le monde !
Tout d'abord désolé pour le silence prolongé, mon stage ne m'a pas laissé beaucoup de temps mais j'ai fait 2-3 trucs lolilols (ca a beau ne pas être français, respectons tout de même l'accord)...
Pour ce qui est de armadillo je n'ai toujours pas eu l'occasion de me pencher à nouveau dessus. Pour me faire pardonner, nous allons aujourd'hui parler du crack me proposé par ESET (lien qui pour une raison complètement inconnue ne fonctionne pas avec Opéra ...) lors de la série de la conférence CONFidence.
Ce challenge a été remporté par Gynvael Coldwind et j00ru qui n'a toujours pas publié sa solution malgré mes menaces de mort répétées donc je le fais à sa place, tant pis pour lui.
Le crack-me en question est packé avec UPX et commence par un petit layer avec des instructions SSE 2 qui embêtent bien olly mais pas le fourbe que je suis qui n'a même pas pris le temps de les étudier mais s'est contenté de lancer le binaire puis de le mettre en pause.
Pas d'anti-debuggers particuliers (aucun ne s'est manifesté avec HideDebugger).
Passons au vif du sujet, la routine de vérification se trouve à l'adresse 00401342 du crack-me qui commence par récupérer le serial, vérifier que sa taille est bien de 32 caractères et enfin le mettre en majuscule.
Le nom est ensuite récupéré et doit faire au moins 3 lettres (pourquoi 3... je ne sais pas ... surement pour faire chier les jB et autres crackers aux pseudos atrophiés (meuh non s'pas méchant)). Enfin le nom est hashé à l'aide de MD2 et des APIs cryptographiques de windows (CryptAcquireContext / CryptCreateHash / CryptHashData / CryptGetHashParam).
Le serial est transformé en hexadécimal à l'aide de la routine en 00401771, rien de spécial.
Une transformation du hash du nom est créé à l'aide de l'algo suivant :
DWORD hh[4]; DWORD h[4] = MD2(Name); d = 0x4E4F44 for (i = 0; i < 4; i++) { d = ROR(d,0xC); hh[i] = ((~(0x45534554^d))&0x455353) | h[i]; }
Et là on entre dans le vif du vif du sujet. Le serial est en effet accepté ou non après l'exécution d'une VM et par la comparaison de sa sortie avec le hash du nom.
La VM est exécutée en 004015C4 et son byte code est situé en 0040305C.
Cette VM n'est pas très compliquée à étudier et on trouve rapidement le format de ses instructions :
grp_1 : *format de l'instruction* aaa ---- ccc --- bbb a==0 => retn a==1 => call if flag(b) a==2 => InvFlag(b) a==3 => SetFlag(b) a==4 => flag(c) = flag(c) & flag(b) a==5 => flag(c) = flag(b) grp_2 : *format de l'instruction* aaa ddddd ccccc bbb a==6 => flag(b) = bit(regs[d], c) a==7 => bit(regs[d], c) = flag(b)
Comme on le voit ici le nombre d'instruction est très réduit, par contre le nombre d'instructions et quand à lui TRES élevé (195,545 instructions, rien que ca).
J'ai donc commencé à coder plusieurs émulateurs en python,
Le premier se contentait de garder en mémoire l'ensemble des opérations faites sur chacun des bits : FAIL python arrivait très vite à bout de mémoire
Le deuxième comportait un simplificateur d'équations booléennes plutôt pas mal foutu mais hélas re FAIL, la complexité des équations explose très vite et mon émulateur n'avance plus.
Pensant que le problème venait de mon simplificateur, je me suis rabattu sur une lib en python implémentant l'algorithme de Quine-McCluskey et ai créé dans la foulée une petite interface de debug mais re-FAIL : la complexité reste trop élevée.
J'ai enfin bridé mon émulateur pour arrêter la simplification des équations à partir d'une certaine complexité et amélioré mon interface de debug et enfin j'ai pu étudier le code exécuté par la VM (tout en sachant que mon émulateur a continué à être modifié / customisé tout au long de l'étude pour effectuer différents tests etc.).
Mon émulateur est disponible à la fin de l'article en pièce jointe et les différentes commandes utilisables sont détaillées ici :
Après un certain temps d'analyse, on arrive enfin à comprendre le fonctionnement du code :
s[0] ^= h[1]; s[2] ^= h[3]; ss[1] = s[1]*hh[2]; ss[3] = s[3]*hh[0]; ss[0] = s[0]*hh[3]; ss[2] = s[2]*hh[1]; ss[1] ^= h[0]; ss[3] ^= h[2];
la multiplication est effectuée à l'aide de la technique de multiplication dans l'Égypte antique.
A la fin de l'exécution, le serial modifié (DWORD ss[4]) doit être égal au hash du nom (DWORD h[4]), l'algorithme est donc simplement inversé à l'aide de l'algo suivant :
DWORD findMagic(h,hh) { DWORD ss = 0, s = 0; int i; for (i = 0; i < 32; i++) { if (getbit(h,i) != getbit(ss,i)) { if (!getbit(hh,i)) { exit(EXIT_FAILURE); } ss += hh; s += 1 << i; } hh <<= 1; } return s; } s[0] = h[1]^findMagic(h[0], hh[3]); s[2] = h[3]^findMagic(h[2], hh[1]); s[1] = findMagic(h[1]^h[0], hh[2]); s[3] = findMagic(h[3]^h[2], hh[0]);
Et enfin comme je me suis bien fait chier à résoudre ce crackme, je me suis dit que j'allais coder un petit keygen (en pièce jointe à la fin de l'article encore une fois), j'espère que vous apprécierai le graphisme et la musique 
Bon, je sais que ce crackme est hyper spécifique et que vous n'aurez pas appris grand chose aujourd'hui si ce n'est comment trouver l'inverse d'un entier modulo 0x100000000 (supaire !) mais j'espère avoir le temps de vous pondre un truc un peu plus intéressant dans les jours / semaines / mois à venir 
Salut les zouaves, aujourd'hui pas vraiment de choses techniques juste une mise en bouche à mon prochain post ainsi qu'une bonne dose de rage...
Voulant me remettre un peu au reverse d'applis flash pour ne pas être spammé de pub quand je veux regarder le dernier épisode de bob l'éponge, j'ai téléchargé un soft qui va bien et que j'avais déjà "étudié".
Il se trouve que le soft en question est protégé par armadillo ayant un peu la flemme de l'unpacker une fois de plus, j'ai cherché si il n'avait pas été released et je suis tombé sur un keygen+patch de DVT.
Sachant que l'application était protégée par Armadillo, j'ai été assez impressionné par cette release et me suis mis en tête de faire la même chose :p
Armadillo utilise les courbes elliptiques pour vérifier le sérial entré, et c'est là que le bas blesse ...
Il se trouve que la librairie de bignum utilisée par Armadillo a surement été codée par un stagiaire d'Ouzbékistan en manque de crack et sous acide. Je ne sais pas si c'est une lib maison ou connue mais c'est assez édifiant ...
Avant de m'étendre là dessus un petit mot sur la courbe utilisée par Armadillo. Déjà plutôt que d'utiliser les courbes "standards" du NIST ou du SECG il fait le malin et utilise une courbe sortie de "nulle part". La seule information que l'on a est l'ordre premier du générateur que l'on trouve dans les SDR : 5192296858534827627896703833467507
Une recherche sur google nous informe que l'ordre premier correspond à une courbe de Koblitz sur GF(2^113) décrite dans les specs WAP WTLS de 1999.
La courbe utilisée est donc : y² + xy = x^3 + x² + 1. Qui est utilisée dans GF(2^113) construit à l'aide du polynôme x^113 + x^9 + 1. (à vérifier pour le polynôme)
On vérifie avec Pari GP (au passage petite pub pour ce merveilleux outil dev par des français (cocorico)) :
(21:47) gp > gf = ffgen(Mod(1,2)*(x^113+x^9+1)) time = 0 ms. %1 = x (21:47) gp > e = ellinit([1,1,0,0,1]) time = 0 ms. %2 = [1, 1, 0, 0, 1, 5, 0, 4, 5, 25, -989, -557, -15625/557, [-1.627524374728508475847551213, 0.1887621873642542379237756065 - 0.7607883796077915001151281354*I, 0.1887621873642542379237756065 + 0.7607883796077915001151281354*I]~, 4.319289634023725525717193601, 2.159644817011862762858596801 - 1.130485447887283043983103252*I, 2.614355328193713389891272325 - 5.04870979 E-29*I, 1.307177664096856694945636163 + 0.7704263744753850346564148888*I, 4.882894076474210213866946808] (21:47) gp > ellpow(e,[gf+1,ellordinate(e,gf+1)[1]],5192296858534827627896703833467507) time = 12 ms. %3 = [0]
youpi !
Bon maintenant il reste à chercher le générateur utilisé (est ce le standard ou un autre ?), à voir comment le sérial est transformé en abscisse, a confirmé que Armadillo utilise bien Nyberg-Rüeppel etc. et pour ça il faut bien mettre les mains dans le code ...
Armadillo ne nous aide pas vraiment, la librairie de bignum utilisée semble être maison (enfin j'espère vu la qualité du code ...), elle est compilée visiblement en mode debugg, il y a des traces de variables de debugg un peu partout, le code n'est ABSOLUMENT PAS optimisé par le compilo etc.
On se retrouve donc à tracer du code dégueulasse sous olly en essayant de défricher un peu le tout à l'aide de IDA et là on tombe sur des énormités sans noms ...
Des BigNums constants créés et détruits à chaque tour de boucle au lieu de les crée une fois avant la boucle et de les détruire après.
Une ENORME routine utilisant les modulos / divisions / décalages / multiplications (le tout sur des bignums je le rappelle ...) pour transformer un BigNums en sa représentation hexadécimale en little endian .... représentation directement accessible dans la structure du bignum, un memcpy aurait suffit ...
Bref, c'est horrible à tracer, c'est une saloperie sans nom et n'ayant pas de twitter pour me plaindre, je me permet de le faire sur mon blog :p
Je vous tiens au courant de mon avancé concernant ce reverse, par contre je suis aussi en stage donc je ne pourrai bosser dessus qu'en dehors de mes horaires de boulot 
Bon je me suis ch** dessus dans mes paramètres de configuration donc je re-compile tout xerces ..
J'en profite donc pour vous bassiner encore un peu avec StarForce en vous donnant un petit truc pour arriver à l'OEP :
Arriver à l'OEP sans encombre : Lancer le programme normalement, quand une fenêtre apparait vous informant qu'il ne vous reste plus que X jours avant expiration, s'attacher au processus avec olly, mettre un BP sur VirtualProtect, continuer l'exécution du processus, le programme break 2 fois sur VirtualProtect, à la troisième fois, mettre un BP sur la page mémoire correspondant au code (F2 dans la fenêtre memory map), virer le breakpoint sur VirtualProtect, reprendre l'exécution et hop magique, on arrive à l'OEP !
Sur ce je m'en vais réviser ma crypto ....
Bon dimanche 
Source: http://baboon.rce.free.fr/index.php?post/2010/03/07/StarForce-%3A-Tip-and-Tricks
Je profite du temps de compilation de xerces pour écrire un petit post à la va vite.
Aujourd'hui nous allons voir une autre feature de StarForce : la redirection de fonction.
J'ai rencontré cette feature après mon post sur la redirection des APIs et quand je l'ai vu j'ai pensé que c'était de la virtualisation de code tant le code de la fonction était horrible et prenait extrêmement de temps à s'exécuter avec plein de loop de partout etc.
J'avais abandonné jusqu'à ce que je tombe sur une application compilée avec VC 7.0 et dont l'une des fonction d'initialisation (donc commune à tout les programmes compilés avec VC 7.0) était redirigée. J'ai donc décidé de me re-pencher dessus avec cette fois ci un point de comparaison.
Pour étudier cette fonction de redirection (que je pensais être une fonction de virtualisation) j'ai posé des HBP en accès sur la valeur de EAX enregistré dans la pile. Cette valeur est baladée en mémoire dans 4-5 emplacement et finalement elle est dépilée et la fonction retourne dans ... la fonction originale ! Ce n'est donc pas une fonction de virtualisation mais bien une fonction de redirection !
Le problème est maintenant de fixer automatiquement ces redirections. On ne peut se baser sur une écriture de la pile, StarForce usant et abusant de call $+5 | pop reg et écrivant de cette façon extrêmement souvent à l'adresse située juste au dessus de l'adresse de retour. Heureusement, malgrès la complexité de la fonction de redirection et de ce petit inconvénient, StarForce utilise toujours la même portion de code pour retourner à la fonction originale. Cette portion de code étant très facilement identifiable, il suffit de faire une recherche en mémoire du pattern, de poser un HBP dessus et on arrive directement à la fonction redirigée !
Code restaurant les registres et retournant à la fonction redirigée :
POP EAX POPFD POP EBP LEA ESP,DWORD PTR SS:[ESP+4] POP EDI POP ESI POP EDX POP ECX POP EBX XCHG DWORD PTR SS:[ESP],EAX RETN
signature de la portion de code : 58 9D 5D 8D 64 24 04 5F 5E 5A 59 5B 87 04 24 C3
Voili voilou, xerces a fini de compiler, je retourne à mon boulot 
Source: http://baboon.rce.free.fr/index.php?post/2010/03/07/Starforce-Again
Salut à tous, je m'embétais un peu ce soir donc j'ai décidé de vous parler de "game hack"
Mes petits frères (et ma grande sœur) me tannaient pour que je leur crack (rooooo) des minis jeux pourris (de zylom pour ne pas le citer) limités à 60 minutes. En ayant marre de devoir cracker un jeux différent par semaine je leur ai bidouillé un loader universel pour tout les jeux de cet éditeur (la protection est rigolote d'ailleurs si vous voulez jeter un coup d'œil)
Bref, pour tester mon loader je suis tombé sur un mini jeu assez addictif (Peggle Nights Deluxe /!\)et, je l'avoue, j'ai passé pas mal de temps dessus hélas une fois le mode aventure terminé, il restait tout plein de coupes à gagner avec des défis etc. et là ca devenait VRAIMENT dur, de plus ma sœur me narguait avec tout ses trophés.
J'ai donc décidé de sortir olly et de voir si il y avait quelque chose à faire ....
Le jeu est assez simple à parcourir, il y a pas mal de références à des chaines de caractères éloquentes du type Electrobolt ou encore ZenBall (faut y jouer pour comprendre ...)
J'ai donc décidé de faire le bourrin : Break Point sur toutes les références à des chaines de caractères intéressantes et lancement du jeux. Dès que l'on break, on remonte le code, on pose des break points sur les sauts conditionnels et on observe le déroulement du programme.
Et très rapidement on trouve l'endroit où la différentiation boule normale et boule bonus est faite, il suffit alors de forcer un saut et toutes les boules deviennent pourvoyeuses de bonus \o/
Mais cela ne me suffit pas ! Je veux aussi le chapeau magique et le triple score !!! Ces bonus agissant aux tours suivants l'obtention du bonus, leur état est stocké en mémoire. Là les software break points ne suffisent plus et il devient nécessaire de poser des hardware break points en accès sur des zones mémoires identifiées à l'aide encore une fois des référence aux chaines de caractère (qui, dans le code, correspondent à l'affichage à l'écran du bonus en cours mais pas de son application, il est donc compliqué de retrouver directement le saut à contourner).
Une fois trouver la routine d'initialisation de ces bonus il suffit de forcer la mise à 1 du flag chapeau magique et triple score plutôt que son écrasement et on obtient une image magique qui ressemble à ça :
Et ça c'est la classe !
(Essayez de faire plus de 50 Millions à peggle nights !)
voila voila, ici s'achève ce pitoyable post sans même une PoC (peut être dans les semaines qui arrivent
pour le moment je fais ca en live avec olly). Si le but premier de ce post était de me la péter avec mon super score, il a aussi pour but de montrer que le RE ce n'est pas seulement des crackmes ou des logiciels ou même des protections mais que ca peut servir un peu à tout y compris à faire rager sa soeur. De plus le """hack""" que j'ai fait ici n'est vraiment pas compliqué et devrait encourager les nouveaux reverser à continuer dans cette voie ;).
Un petit post rapide pour vous présenter une protection que j'ai rencontré il y a peu (dans un bar, il faisait chaud, nous étions seuls, on s'est rapproché, je lui ai payé un verre, tous ca quoi).
Visiblement son concepteur sans être un pro de sécu savait qu'il existait de méchants keygenners qui allaient lui voler son travail et donc plutôt que de vérifier son serial avec une bête fonction facilement reversable et plutôt que de s'embeter avec des grosses librairies de crypto, des packers trop chers, il a décidé de faire son propre systeme de protection.
Le serial est convertit en un DWORD % 0x10000 et est utilisé comme offset, il écris alors un 1 à l'emplacement et continue son exécution normalement (ou pas si le 1 a écrasé une variable importante ...). Il va ensuite au moment de la génération d'images (c'est un soft de manipulation vidéo) vérifier si une variable globale est égale à 1 (la fameuse variable censée être initialisée par le serial) si ce n'est pas le cas, il insère un watermark. Le code insérant le watermark étant difficile à retrouver (pas d'APIs, noyé au milieu d'autres fonctions) il est compliqué de retrouver la variable globale testée et donc de générer des serials valides.
Bien sur on y arrive quand même avec un peu de temps, de plus si un serial est volé / cardé il devient très simple de connaitre l'adresse. Néanmoins pour un petit soft peu distribué, et face à des crackers pas très motivé ça à l'air de suffire amplement ... (aucun crack depuis la sortie du soft il y a 2 ans)
Voila voila, bonne journée 
Source: http://baboon.rce.free.fr/index.php?post/2010/02/12/Protection-rigolote