# JL/JGE Intel CPU bug as anti-reversing trick

months ago Bow Sineath (a very clever reverser!) asked me: “does JL [jump is less] instruction check ZF flag?” I said: “well, give me a second to think, well, it’s supposed to check it, otherwise it would act like JLE [jump if less or equal] and besides, JL is synonym of JNGE (jump if not great or equal), so JL should check ZF!“.

but, according to Intel’ manuals JL and JNGE check only if SF != OF. CMOVL/CMOVNGE work the same way. at that time I thought that it’s just a documentation bug and even pointed this out in my presentation on HITB 2008 conference.

fragment of Intel manual

fragment of Intel' manual

but I was wrong!!! I have checked it and found out that JL/JNGE does not check ZF flag!!! to do this I wrote extremely simple POC (if you’re too lazy to type, download source and binary):

__asm
{
mov eax, 002C2h ; S = 1, O = 0, Z = 1
push eax
popfd
jl jump_is_taken ; ==>
mov p, offset noo
jump_is_taken:
}

mov eax, 2C2h/push eax/popfd set SF with ZF and clear OF. so, SF != OF, but ZF is set. what CPU is going to do? easy to check with Olly! just load the program and start tracing. ops!!! JL is taken!!! JL ignores ZF!!! x86emu (plug-in for IDA-Pro) acts the same. didn’t check other emulators yet.

well, it’s interesting. why JL (and similar commands) ignores ZF?! guess, normal CPU command (like TEST/CMP/XOR/etc) never set ZF if result is less, so JL just ignores it. but… if we set flags manually or use other tricks… it becomes a real trap!!! consider the listing above and ask your co-worker: is the jump taken or not? I’m sure, some of them will answer: of course, the jump is not going to be taken! a good anti-reversing trick!!! I just wonder – how software is still working on buggy hardware.

JL does not check ZF flag as it is supposed to do!!!

JL does not check ZF flag as it is supposed to do!!!

 

7 Comments

  1. Man, you never rest! nice find.
    yeah, Intel just made sure that cmp,test instructions comply with jl :-P

  2. “Buggy hardware”?! Who says JL is “supposed” to check the ZF flag?

    Your flags specify that the number is *negative* and *also zero at the same time*. They had to chose one, and chose the simpler one.

  3. “I just wonder – how software is still working on buggy hardware.”

    No wonder if hardware isn’t buggy (at least in this case) :)
    It does exactly what is written in manual.

    And if it doesn’t do what you expect to do, that’s another problem :)

  4. Why would JL check ZF? It’s just checking for the sign.
    Just like JB does not check ZF, it’s just checking for the carry.
    JLE will check ZF, just like JBE will.

  5. I think Kris is right. If Z flag is set, then, some, last, previous command said to us that two its operands are equal (not one less than other, as JL supposed to exactly work). But we still have got a jump.

  6. Peter is right, there is no reason for JL to check ZF.

    In the example Kris posted, the branch is taken as expected since SF OF.

    I asked Kris the question when I encountered something like this:

    “test eax, eax
    jl [offset]”

    I had forgotten that test sets the value of SF to the MSB of the result and assumed this was some screwy way of checking if the value in eax was 0 or not (which in a sense it does because if eax == 0 and a “test eax, eax” is encountered, it will set the SF to 0 since the MSB of the result is 0, which results in the branch not being taken), which was incorrect.

  7. No, Kris is wrong. If the S flag is set, then the value cannot be == zero, so it must be less than zero, and so the branch is taken.
    If the S flag is not set, then the branch is not taken, because at least of the other conditions is true. Intel does not need to test which one it is.

Leave a comment

Comments are closed.