matsp: Yes, yes, I know. I was using POP CS as an ideological thing (IIRC I wanted to use that particular instruction when I was playing about with assembler a few months ago and was outraged to find that I couldn't! :().
I've looked at the RET thing and could feasibly track those as well, although this would start to get to the point where I'd be generating exceptions every few instructions, hmm :o
Sebastiani: Aha, but seeing as this is my own executable format, what say I specify that the code pages cannot contain any data? I expect to be able to identify each and every byte as an instruction or part of an instruction and if I can't... well, the code won't be run. ;)
This avoids the whole halting problem as far as the scanner is concerned. All it needs to know is the size of all instructions.
I suppose that my idea is effectively a VM with the processing for free (the code itself is already native, but the resources involved aren't).
It s possible to POP CS, although not specifically with that mnemonic. Simply PUSH the value onto the stack and execute an IRET, although this also pops IP and causes execution to continue at the target address.
You do not need to use IRET (in fact, that would probably not WORK), but a "far ret" would work fine, and "pop" CS. But the point was more that there is no instruction in x86 that restored CS on it's own from the stack (or for that matter "loads" CS from memory, or some such). You have to use one of the JUMP, CALL or RET instructions that take a CS:rIP pair - or use task-switching instructions).
Originally Posted by abachler
As to DS and CS being the same, I was referring to x86. Most other processors do not have DS and CS register. Of course, there are at least some processors that have a harvard or pseudo-harvard architecture. A good way to avoid accidentally executing data!
Note also that my simple push, push, ret example is just ONE of many different ways that we could come up with a "stack-frame that points to a function". You most likely would have to completely follow the whole of the instruciton flow to know what's going on. A more complex example:
[I don't guarantee that the above code is actually CORRECT - but something along those lines WILL be able to execute correctly].
mov ebp, esp
mov eax, 0xFFFFFFFF
xor eax, ebp
sub eax, 4
mov [eax], a
sub eax, 4
mov [eax], b
xchg esp, eax
By the time you can follow such things, you have pretty much build a complete x86 instructin simulator. Which is a MAJOR task.
I'm still trying to wrap my head around that one! But you know, it also just goes to show that there really are a lot of paradigms that higher level languages could employ in implementing certain features. You just don't think of that much unless you've worked a lot with assembly, I guess.
tbh I've always doubted that I could make this, because if we can just watch a couple instructions to get safe full-speed execution without coming up with our own instruction set, someone would deffo have done it before me. I'm not the sharpest, unfortunately. ;)
However there only so many instructions that are used for flow control and specifically for changing CS and/or EIP, you could get to a point where you could at least protect against "accidental" bad addresses, even if the system could be defeated in a particular manner.
This question was effectively me asking myself (referring to you) "why" I am making the VM that I am making when I could to some extents cheat. The answer is that it's the only way to be sure (you can't mess with a VM when the native code only exists at runtime)
Sure, it comes down to "what are you preventing" - if you are trying to prevent "single diigt age" kids from eating the biscuits, or are you trying to prevent "professional" criminals from robbbing a bank, or professional spies from stealing military secrets.
The solution to the first is not a solution to the second or third threat (although the other way around probably is!)
And a common problem in secure computing, by the way - who can you trust, and what can you trust them with.
Yes, I was refering to the status opins ont eh x86, which cna be used to restrict memory accesses based on teh segment register used for the address. This effectively gives access to increased memory addressability, although afaik it was only used in some industrial computers back in teh 8086 days to extend memory to 4MB and to give multiprocessors access to a shared memory region through the ES register. This of course requires a processors without cache or paging enabled.