flat assembler
Message board for the users of flat assembler.
 Home   FAQ   Search   Register 
 Profile   Log in to check your private messages   Log in 
flat assembler > Main > Snippets for detection of 32-bit/64-bit code segment

Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6676
Location: Kraków, Poland
Snippets for detection of 32-bit/64-bit code segment
I accidentally came across this little snippet: https://twitter.com/VK_Intel/status/897206671231131649.*
And it made me smile, because it is so reminiscent of the tricks I've been using back in early 2000's when playing with first versions of my 32-bit unreal mode, where the interrupt handler needed to detect if the code segment is 32-bit or 16-bit and deal with it accordingly. You can read about it in my unREAL mode article.

Unlike 16/32, I never needed to do a similar thing to distinguish 32-bit from 64-bit. The only obvious application that comes to my mind would be malware (though the world of code is complex out there, so I do not rule it out that someone might use it for other obscure purpose). But still, this one made me think a little. I wondered if I could make a variant using the same principle as the original 16/32 "distinguisher" I made for my unreal mode.

You see, the one I made for interrupt handler did it without altering any register. The flags are stored automatically on the stack when interrupt is initiated, so I did not have to worry about them. So I used the CMP instruction, though it was not my intention to use the result for a conditional jump. The trick was that CMP used immediate operand, which was decoded as either 16-bit or 32-bit depending on mode, so there was a difference of two bytes in the interpreted instruction length, and two bytes is exactly what is needed to fit the "JMP SHORT" instruction. This jump would be seen in 16-bit mode, but in 32-bit one it would become part of the immediate of the dummy CMP instruction. It was a "conditional unconditional" jump.

We cannot use the exact same trick to distinguish 64-bit from 32-bit, as they both use the same immediate sizes for instructions like CMP. We could try CMP with a memory operand, because 67h prefix does switch to 16-bit addressing in 32-bit mode and to 32-bit addressing in 64-bit mode. The problem is, the memory address could be invalid. I can use LEA to avoid the exception, but this is going to trash a register:

Code:
        use64
        lea     eax,[esi]               ; 67 8D 06
        jmp     short detected_64       ; EB xx

; in 32-bit mode this becomes:

        use32
        lea eax,[word 0xxEBh]           ; 67 8D 06 EB xx



So, this variant uses the same principle, hiding a short jump within a code of dummy instruction, but this time I could not avoid altering a register.

Wait a second, in modern CPUs we have that NOP instruction with a dummy memory operand, which was created to be used in alignments...

Code:
        use64
        nop     [esi]                   ; 67 0F 1F 06
        jmp     short detected_64       ; EB xx

; in 32-bit mode this becomes:

        use32
        nop     [word 0xxEBh]           ; 67 0F 1F 06 EB xx

Now, this is byte longer, but it preserves all the registers and even flags. If we needed an interrupt handler that would work the same in 32-bit and 64-bit mode... Wait, what?! Very Happy

Have a nice day, everyone!


___
* This is neither the original nor the best reference. Digging deeper leads to this place, among others: https://stackoverflow.com/questions/38063529/x86-32-x86-64-polyglot-machine-code-fragment-that-detects-64bit-mode-at-run-ti/. But I kept the first one just because it mentions fasm. Wink
Post 19 Nov 2017, 13:20
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 15303
Location: Bigweld Industries
Cool Nice trick.
Post 19 Nov 2017, 13:31
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 891
This is pretty cool. I think you can also switch between modes (16/32/64) in user mode programs with CS register, but of course you won't be able to use libraries. (also, it depends on the OS)
Post 19 Nov 2017, 13:42
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 15303
Location: Bigweld Industries

Furs wrote:
I think you can also switch between modes (16/32/64) in user mode programs with CS register ...

You can't get all three together though. IIRC only the 16/32 and 32/64 combinations are possible. No 16/64 or 16/32/64.
Post 19 Nov 2017, 13:54
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 891
Doesn't that depend on the OS? Also, shouldn't it be possible 100% all 3 of them? I mean, all 3 modes are valid in Long Mode, so there has to be a way to switch to it (even if, obviously, undocumented and unreliable between OS versions). Assuming it's not during kernel mode, which wouldn't make much sense to me (I mean, does Linux have 16-bit code in kernel? because it can run 16-bit Windows apps with Wine; Windows' limitation of lack of 16-bit in 64-bit comes from its APIs due to HANDLEs, nothing to do with the CPU).
Post 19 Nov 2017, 20:44
View user's profile Send private message Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 891
https://en.wikipedia.org/wiki/Long_mode here it says it can run 16-bit protected mode code, I've never actually fiddled with this stuff, so maybe I'm assuming something stupid Razz (maybe Linux does have special cases for Wine?)

Interesting read: https://www.dkia.at/en/node/180 seems you can Wink
Post 19 Nov 2017, 20:49
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6676
Location: Kraków, Poland
I consulted the old manuals to be sure - yes, both AMD and Intel specifications allow D bit to switch to 16-bit code in the compatibility mode. But the Virtual 8086 mode does not work (perhaps because it messes too much with the addressing). So it should be possible to run 16-bit protected mode applications (like the ones for Windows 3), but not DOS programs, as they would require V86 mode.
Post 19 Nov 2017, 20:58
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6676
Location: Kraków, Poland
So, to continue the (pointless?) exercise, the trick can be easily extended to a three-way switch:

Code:
        use64

        nop     [esi]                   ; 67 0F 1F 06
        jmp     short no32              ; EB xx

        ;  32-bit detected

   no32:
        nop     [rsi]                   ; 0F 1F 06
        jmp     short is64              ; EB xx

        ; 16-bit detected

   is64:

        ; 64-bit detected

And if we allowed the code to destroy some registers and flags, there would be many other interesting options, perhaps:

Code:
        use16
        dec     ax              ; 48
        mov     ax,0            ; B8 00 00
        jmp     short is16      ; EB xx
        jmp     short is32      ; EB xx
        jmp     short never     ; EB xx
        jmp     is64

Hmm, I'm not sure if I like this one.
Post 19 Nov 2017, 21:32
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6676
Location: Kraków, Poland
This method is also closely related to the problem of having a snippet that executes as different instructions depending on what model of CPU is it run on. I'd been thinking that this was the thing of the past that died with the advent of CPUID and reliably signalled #UD, but recently I was reminded that there are instructions interpreted by Intel 64 differently than by the original AMD's x86-64.

So when consulting that 16-bit thing I went to both manuals, it is better to check twice, sometimes an interesting discovery may be made.
Post 19 Nov 2017, 21:46
View user's profile Send private message Visit poster's website Reply with quote
Furs



Joined: 04 Mar 2016
Posts: 891
But wasn't push with 16-bit operand supposed to be valid in 64-bit mode? I mean it specifically said you can't encode 32-bit but instead 16-bit pushes.
Post 20 Nov 2017, 16:18
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6676
Location: Kraków, Poland

Furs wrote:
But wasn't push with 16-bit operand supposed to be valid in 64-bit mode? I mean it specifically said you can't encode 32-bit but instead 16-bit pushes.

It was in the original x86-64 specification by AMD. But when Intel made its own version, they made some changes, like removing the 16-bit pushes and jumps. And at the same time they added 10-byte immediate far jump that AMD was missing.

This board has a long history, if you dig deep enough you may find discussion of these details from the time when 64-bit instructions were being implemented into fasm.
Post 20 Nov 2017, 17:00
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >

Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001-2005 phpBB Group.

Main index   Download   Documentation   Examples   Message board
Copyright © 2004-2016, Tomasz Grysztar.