Map and projects (the most frequently updated page of this blog)

2010/01/13

They say jump, you say how high

Various ways of JMPing
jumping, aka branching, is one of the most common operations.

I wrote a file that implements many forms of jumping, whether they are common, obfuscated, or rare. Not everything is detailed in this post, check the source for further information.

First, Jumps,

EB 07 JMP SHORT 004000F9
E9 07000000 JMP 00400105
FFE7 JMP EDI ; 00400113
FF25 19014000 JMP DWORD PTR DS:[400119] ; 00400124
EA 32014000 1B00 JMP FAR 001B:00400132
FF2D 38014000 JMP FAR DS:[400138] ; DS:[00400138]=001B:00400145

then CALLs,

E802 CALL 00400103
FF1A CALL FAR [EDX]
FF12 CALL [EDX]
FFD2 CALL EDX
9A 7C014000 1B00 CALL FAR 001B:0040017C

RETurns

C3 RETN ; Return to 004001DE
CB RETF ; Return to 001B:004001EC
CF IRETD ; Return to 001B:004001FB, flags = 206

loop*.

Then, another way of jumping (unconditionally) is to either fake the condition by assuming a start value, or to force it, by setting a register or a flag (pushf, lahf, stc, SEH, iret).

Note that all of them (jumps, jxx, calls, return, loops) are influenced by the operand prefix: it turns dword operands in word, of course, but you will jump to the lower bits of EIP. And as expected, CALL WORD will only push a word on the stack.

004000F0:
66:EB 00 JMP SHORT 000000F3
66:E2 00 LOOPD SHORT 000000F6
66:E8 0000 CALL 000000FA
66:C3 RETN ;Return to 00006D4F, [ESP] = 7C816D4F

Naturally, any of those WORD jumps will be unlikely useful in your code, but it's an unusual way to trigger an exception.

LOOP and JECXZ are also influenced by the address prefix (like REP), which turns the condition on CX only, as you could expect. But, unlike jcxz, neither 67 Loop* or 67 Rep* have an official name (OllyDbg calls it LOOPW, as opposed to LOOPD, though).

With the same ECX value:
67:E2 02 LOOPW SHORT 004001CA ;Loop is NOT taken, CX=0001
E2 07 LOOPD SHORT 004001D1 ;Loop is taken, ECX=FFFF0001

You can also play with the stack, calling code there, pushing then modifying values.

Next are the unusual (more like anti-emulators) ways:
using an API that expects a callback function, spawning a extra thread, or using exceptions.

Last is the ability to create your own segment (local descriptor), and jump to it. With the right values (HighWord.Bits.Type), your debugger might not even be able to display the code being executed, which is quite unusual).

While the rest of the file is pretty easy, the SetLdtEntries parameter are the most difficult part to understand, so I suggest reading Nicolas' explanation if you're curious.

Big thanks to Peter Ferrie for his help (66, 67, SetLdtEntries)

Binary Source

[...]

Diverses façons de sauter
Sauter (jump, branch) est une des opérations les plus répandues.

J'ai écrit un fichier qui implémente plusieurs façons de sauter, qu'elles soient communes, dissimulée ou rare. Tous n'est pas détaillé dans ce billet, allez voir le code source pour plus d'informations.

Tout d'abord, les JMP

EB 07 JMP SHORT 004000F9
E9 07000000 JMP 00400105
FFE7 JMP EDI ; 00400113
FF25 19014000 JMP DWORD PTR DS:[400119] ; 00400124
EA 32014000 1B00 JMP FAR 001B:00400132
FF2D 38014000 JMP FAR DS:[400138] ; DS:[00400138]=001B:00400145

puis les CALL,

E802 CALL 00400103
FF1A CALL FAR [EDX]
FF12 CALL [EDX]
FFD2 CALL EDX
9A 7C014000 1B00 CALL FAR 001B:0040017C

les RETours

C3 RETN ; Return to 004001DE
CB RETF ; Return to 001B:004001EC
CF IRETD ; Return to 001B:004001FB, flags = 206

les boucles (LOOP).

Ensuite, une autre façon de sauter (inconditionnellement) est de feindre une condition, en assumant une valeur initiale, ou de la forcer, en définissant un registre ou un drapeau (pushf, lahf, stc, SEH, iret).

Il faut remarquer qu'ils sont tous (jumps, jxx, calls, return, loops) influencés par le préfixe d'opérandes : bien sur ça transforme les opérandes double mots en mot, mais on sautera a IP, les octets supérieurs étant nul. De plus, CALL WORD va simplement mettre un mot sur la pile, comme on s'y attend.

004000F0:
66:EB 00 JMP SHORT 000000F3
66:E2 00 LOOPD SHORT 000000F6
66:E8 0000 CALL 000000FA
66:C3 RETN ;Return to 00006D4F, [ESP] = 7C816D4F

Evidemment, aucune de ces sauts sur un mot ne semble utile dans du code normal, mais c'est un moyen inhabituel de déclencher une exception.

Loop et Jecxz sont aussi influencés par le préfixe d'adresse (comme REP), qui mettra la condition sur CX uniquement. Mais contrairement à jecxz/jcxz, ni Loop ni Rep n'ont de nom officiel sur un mot (OllyDbg les appelle LoopW, par opposition a Loopd)

avec la meme valeur pour ECX :
67:E2 02 LOOPW SHORT 004001CA ;Loop is NOT taken, CX=0001
E2 07 LOOPD SHORT 004001D1 ;Loop is taken, ECX=FFFF0001


On peut aussi jouer avec la pile, y exécuter du code, empiler puis modifier des valeurs.

Ensuite viennent les moyens moins courant (plutôt juste des anti-émulateurs):
appeler une API qui utilise un callback, créer un nouveau fil, ou utiliser les exceptions.

Enfin, la possibilité de créer votre propre segment (descripteur local) et d'y sauter. Avec les bonnes valeurs (HighWord.Bits.Type), on peut même empêcher votre débogueur d'afficher le code en cours d'exécution, ce qui est plutôt inhabituel.

La majeure partie du code source est plutôt facile à comprendre, mais les paramètres de SetLdtEntries sont bien compliques, donc allez voir l'explication de Nicolas si vous voulez en savoir plus.

Un grand merci à Peter Ferrie pour son aide (66, 67, SetLdtEntries)

Binaire Source

No comments:

Post a Comment