Confused: VERR, ZF, AC and JE. What is going on?

  • Follow


I am working on a memory allocation monitor, and part of the project
involves hand-writing some assembly to copy some memory.

I have just a quick question which has me very confused. Here is a
simple piece of code I am working on to make sure my rep movs bounds
are addressed to valid memory segments:

// Bounds checking (ecx contains the copy source address)
verr cx // According to documentation, this should set ZF = 1 if
segment cx is readable.
          // When I am debugging, flag AC is being set = 1, which I
understand to be
          // the same as ZF.
jnz <label3>// According to documentation, "Jump near if not zero
(ZF=0)".
                 // So it should skip to <label3:> if cx is not
readable.
                 // When debugging it is jumping if AC = 0 or AC = 1.
add ecx, <copySize>
verr cx
jnz <oabel3>
sub ecx, <copySize>

// Perform the copy of <freeCopySize> bytes
mov esi, ecx // Set source address
mov edi, eax // destination address
mov ecx, <copySize> // number of byte to copy
rep movs byte [edi], byte [esi]
fclex
<label3:>



My problem is in the bound checking and my use of jnz and verr. Anyone
know why it isn't working? According to the documentation I thought
this would work.
0
Reply Geoff 8/1/2009 7:57:57 PM

Geoff wrote:
> I am working on a memory allocation monitor, and part of the project
> involves hand-writing some assembly to copy some memory.
> 
> I have just a quick question which has me very confused. Here is a
> simple piece of code I am working on to make sure my rep movs bounds
> are addressed to valid memory segments:
> 
> // Bounds checking (ecx contains the copy source address)
> verr cx // According to documentation, this should set ZF = 1 if
> segment cx is readable.

Correct, but it is not obvious that cx is a segment. It appears to be 
the low order bits of the offset, which is not what you want to "verr".

>           // When I am debugging, flag AC is being set = 1, which I
> understand to be
>           // the same as ZF.

Not correct. AC is a completely different flag - not useful in this 
instance, I don't think.

> jnz <label3>// According to documentation, "Jump near if not zero
> (ZF=0)".

Right. You could be using a "short" jump there, but aside from the size 
of the code, it shouldn't make a difference. Your assembler may well be 
giving you a short jump anyway. (worth noting that jz and je are two 
names for the same instruction)

>                  // So it should skip to <label3:> if cx is not
> readable.

Sounds right... if cx is a segment.

>                  // When debugging it is jumping if AC = 0 or AC = 1.

Shouldn't make a difference what AC is - jz/jnz watches ZF.

> add ecx, <copySize>
> verr cx
> jnz <oabel3>
> sub ecx, <copySize>
> 
> // Perform the copy of <freeCopySize> bytes
> mov esi, ecx // Set source address
> mov edi, eax // destination address
> mov ecx, <copySize> // number of byte to copy
> rep movs byte [edi], byte [esi]

This moves from [ds:esi] to [es:edi], so ds and es would be the segments 
to check. They almost certainly are readable and writeable, unless 
you're doing something very unusual.

> fclex

Is this supposed to have something to do with the copy???

> <label3:>
> 
> 
> 
> My problem is in the bound checking and my use of jnz and verr. Anyone
> know why it isn't working? According to the documentation I thought
> this would work.

What OS is this for, Geoff? The "common" OSen... Linux, Windows, *BSD, 
MacOS... have some common features - cs is readonly, generally, ds, es, 
and ss are read/write - fs and gs may or may not contain a valid 
selector. Read/write access is controlled by the paging mechanism, and I 
doubt if verr/verw will help. If you're "under the hood", or in "MyOS", 
this may not be true. We may need more information to help you...

Best,
Frank
0
Reply Frank 8/1/2009 9:10:05 PM


On Aug 1, 12:57=A0pm, Geoff <glmcd...@MUNGED.microcosmotalk.com> wrote:
> I am working on a memory allocation monitor, and part of the project
> involves hand-writing some assembly to copy some memory.
>
> I have just a quick question which has me very confused. Here is a
> simple piece of code I am working on to make sure my rep movs bounds
> are addressed to valid memory segments:
>
> // Bounds checking (ecx contains the copy source address)
> verr cx // According to documentation, this should set ZF =3D 1 if
> segment cx is readable.
> =A0 =A0 =A0 =A0 =A0 // When I am debugging, flag AC is being set =3D 1, w=
hich I
> understand to be
> =A0 =A0 =A0 =A0 =A0 // the same as ZF.
> jnz <label3>// According to documentation, "Jump near if not zero
> (ZF=3D0)".
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// So it should skip to <label3:> if c=
x is not
> readable.
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// When debugging it is jumping if AC =
=3D 0 or AC =3D 1.
> add ecx, <copySize>
> verr cx

Do you understand anything in segmentation? How is it that the same
register (E)CX contains both the segment selector (on which you're
doing VERR) and the amount of data to copy (copySize for REPMOVSB)?
Seems like you're expecting VERR to validate the data offsets or both
the segment selector and data offsets, but it validates only the
segment selector.

> jnz <oabel3>
> sub ecx, <copySize>
>
> // Perform the copy of <freeCopySize> bytes
> mov esi, ecx // Set source address
> mov edi, eax // destination address
> mov ecx, <copySize> // number of byte to copy
> rep movs byte [edi], byte [esi]
> fclex
> <label3:>
>
> My problem is in the bound checking and my use of jnz and verr. Anyone
> know why it isn't working? According to the documentation I thought
> this would work.

You didn't understand the document and assumed something that wasn't
there.

Alex
0
Reply Alexei 8/1/2009 11:45:06 PM

On Aug 1, 3:10=A0pm, Frank Kotler <fbkot...@MUNGED.microcosmotalk.com>
wrote:
> Geoff wrote:
> > I am working on a memory allocation monitor, and part of the project
> > involves hand-writing some assembly to copy some memory.
>
> > I have just a quick question which has me very confused. Here is a
> > simple piece of code I am working on to make sure my rep movs bounds
> > are addressed to valid memory segments:
>
> > // Bounds checking (ecx contains the copy source address)
> > verr cx // According to documentation, this should set ZF =3D 1 if
> > segment cx is readable.
>
> Correct, but it is not obvious that cx is a segment. It appears to be
> the low order bits of the offset, which is not what you want to "verr".
>
> > =A0 =A0 =A0 =A0 =A0 // When I am debugging, flag AC is being set =3D 1,=
 which I
> > understand to be
> > =A0 =A0 =A0 =A0 =A0 // the same as ZF.
>
> Not correct. AC is a completely different flag - not useful in this
> instance, I don't think.
>
> > jnz <label3>// According to documentation, "Jump near if not zero
> > (ZF=3D0)".
>
> Right. You could be using a "short" jump there, but aside from the size
> of the code, it shouldn't make a difference. Your assembler may well be
> giving you a short jump anyway. (worth noting that jz and je are two
> names for the same instruction)
>
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// So it should skip to <label3:> if=
 cx is not
> > readable.
>
> Sounds right... if cx is a segment.
>
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0// When debugging it is jumping if A=
C =3D 0 or AC =3D 1.
>
> Shouldn't make a difference what AC is - jz/jnz watches ZF.
>
> > add ecx, <copySize>
> > verr cx
> > jnz <oabel3>
> > sub ecx, <copySize>
>
> > // Perform the copy of <freeCopySize> bytes
> > mov esi, ecx // Set source address
> > mov edi, eax // destination address
> > mov ecx, <copySize> // number of byte to copy
> > rep movs byte [edi], byte [esi]
>
> This moves from [ds:esi] to [es:edi], so ds and es would be the segments
> to check. They almost certainly are readable and writeable, unless
> you're doing something very unusual.
>
> > fclex
>
> Is this supposed to have something to do with the copy???
>
> > <label3:>
>
> > My problem is in the bound checking and my use of jnz and verr. Anyone
> > know why it isn't working? According to the documentation I thought
> > this would work.
>
> What OS is this for, Geoff? The "common" OSen... Linux, Windows, *BSD,
> MacOS... have some common features - cs is readonly, generally, ds, es,
> and ss are read/write - fs and gs may or may not contain a valid
> selector. Read/write access is controlled by the paging mechanism, and I
> doubt if verr/verw will help. If you're "under the hood", or in "MyOS",
> this may not be true. We may need more information to help you...
>
> Best,
> Frank

Thanks for the quick reply Frank.

It looks like I made a major concept mistake as to what the segment
selector was. I thought it was the high 16 bits of the memory address
(although I was accidentally using the low 16 bits). I will do some
research on this to understand segment selectors properly.

This is for the windows operating system. Do you think this is the
best approach to figuring out if the 'rep movs' will cause a memory
access exception, or is there a better way of doing it? I know the
destination is valid, but not the source.

'fclex' was in there by accident. I just added that as a test to try
and make it ignore access violations from the previous instruciton.



0
Reply Geoff 8/2/2009 12:09:43 AM

In article <4a74d947$0$4947$9a6e19ea@unlimited.newshosting.com>, 
glmcdona@MUNGED.microcosmotalk.com says...

[ ... ]

> It looks like I made a major concept mistake as to what the segment
> selector was. I thought it was the high 16 bits of the memory address
> (although I was accidentally using the low 16 bits). I will do some
> research on this to understand segment selectors properly.

The selector is the top 16 bits of a 48-bit address.
 
> This is for the windows operating system. Do you think this is the
> best approach to figuring out if the 'rep movs' will cause a memory
> access exception, or is there a better way of doing it? I know the
> destination is valid, but not the source.

Inside of Windows, you'll almost never see anything but a 32-bit 
address. The basic setup is that CS, DS, ES and SS are all set up 
with a base of 0 and a limit of 4 Gig. Inside of that address space, 
only paging is used to determine what's readable, writable, etc.

-- 
    Later,
    Jerry.
0
Reply Jerry 8/2/2009 1:09:52 AM

On Aug 1, 5:45=A0pm, "Alexei A. Frounze"
<alexfrun...@MUNGED.microcosmotalk.com> wrote:
> On Aug 1, 12:57=3DA0pm, Geoff <glmcd...@MUNGED.microcosmotalk.com> wrote:
>
>
>
>
>
> > I am working on a memory allocation monitor, and part of the project
> > involves hand-writing some assembly to copy some memory.
>
> > I have just a quick question which has me very confused. Here is a
> > simple piece of code I am working on to make sure my rep movs bounds
> > are addressed to valid memory segments:
>
> > // Bounds checking (ecx contains the copy source address)
> > verr cx // According to documentation, this should set ZF =3D3D 1 if
> > segment cx is readable.
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 // When I am debugging, flag AC is being =
set =3D3D 1, w=3D
> hich I
> > understand to be
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 // the same as ZF.
> > jnz <label3>// According to documentation, "Jump near if not zero
> > (ZF=3D3D0)".
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0// So it should sk=
ip to <label3:> if c=3D
> x is not
> > readable.
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0// When debugging =
it is jumping if AC =3D
> =3D3D 0 or AC =3D3D 1.
> > add ecx, <copySize>
> > verr cx
>
> Do you understand anything in segmentation? How is it that the same
> register (E)CX contains both the segment selector (on which you're
> doing VERR) and the amount of data to copy (copySize for REPMOVSB)?
> Seems like you're expecting VERR to validate the data offsets or both
> the segment selector and data offsets, but it validates only the
> segment selector.
>
> > jnz <oabel3>
> > sub ecx, <copySize>
>
> > // Perform the copy of <freeCopySize> bytes
> > mov esi, ecx // Set source address
> > mov edi, eax // destination address
> > mov ecx, <copySize> // number of byte to copy
> > rep movs byte [edi], byte [esi]
> > fclex
> > <label3:>
>
> > My problem is in the bound checking and my use of jnz and verr. Anyone
> > know why it isn't working? According to the documentation I thought
> > this would work.
>
> You didn't understand the document and assumed something that wasn't
> there.
>
> Alex

Your right, I misunderstood the segmentation.

But ecx during the bound checking is not the size to copy, it was
changed during instruction:
mov esi, ecx // Set source address

So am not quite a complete idiot, I just thought the segment selector
was the high 16 bits of the memory address.
0
Reply Geoff 8/2/2009 1:10:22 AM

On Aug 1, 5:45=A0pm, "Alexei A. Frounze"
<alexfrun...@MUNGED.microcosmotalk.com> wrote:
> On Aug 1, 12:57=3DA0pm, Geoff <glmcd...@MUNGED.microcosmotalk.com> wrote:
>
>
>
>
>
> > I am working on a memory allocation monitor, and part of the project
> > involves hand-writing some assembly to copy some memory.
>
> > I have just a quick question which has me very confused. Here is a
> > simple piece of code I am working on to make sure my rep movs bounds
> > are addressed to valid memory segments:
>
> > // Bounds checking (ecx contains the copy source address)
> > verr cx // According to documentation, this should set ZF =3D3D 1 if
> > segment cx is readable.
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 // When I am debugging, flag AC is being =
set =3D3D 1, w=3D
> hich I
> > understand to be
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 // the same as ZF.
> > jnz <label3>// According to documentation, "Jump near if not zero
> > (ZF=3D3D0)".
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0// So it should sk=
ip to <label3:> if c=3D
> x is not
> > readable.
> > =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0 =3DA0// When debugging =
it is jumping if AC =3D
> =3D3D 0 or AC =3D3D 1.
> > add ecx, <copySize>
> > verr cx
>
> Do you understand anything in segmentation? How is it that the same
> register (E)CX contains both the segment selector (on which you're
> doing VERR) and the amount of data to copy (copySize for REPMOVSB)?
> Seems like you're expecting VERR to validate the data offsets or both
> the segment selector and data offsets, but it validates only the
> segment selector.
>
> > jnz <oabel3>
> > sub ecx, <copySize>
>
> > // Perform the copy of <freeCopySize> bytes
> > mov esi, ecx // Set source address
> > mov edi, eax // destination address
> > mov ecx, <copySize> // number of byte to copy
> > rep movs byte [edi], byte [esi]
> > fclex
> > <label3:>
>
> > My problem is in the bound checking and my use of jnz and verr. Anyone
> > know why it isn't working? According to the documentation I thought
> > this would work.
>
> You didn't understand the document and assumed something that wasn't
> there.
>
> Alex

Your right, I made a mistake in my understanding of a segment
selector. I thought it was the high 16 bits of a memory address.

I am actually not using the size to copy as the segment selector, I am
using the low 16  bits of the memory address copy source. The
registers are swapped at:
mov esi, ecx // Set source address
0
Reply Geoff 8/2/2009 1:11:04 AM

Geoff wrote:

.....
> Thanks for the quick reply Frank.

Heh! Inside track - I'm one of the moderators of this group. We *try* to 
get *all* messages approved as quickly as possible, but it's easier if 
we "know it's coming". Apologies to everyone who was on the "whitelist" 
- we haven't implemented that as yet, so there will be more delay than 
previously. (our previous moderator, Chuck Crayne, passed away 
unexpectedly... we really miss him!)

> It looks like I made a major concept mistake as to what the segment
> selector was. I thought it was the high 16 bits of the memory address
> (although I was accidentally using the low 16 bits). I will do some
> research on this to understand segment selectors properly.

As Jerry pointed out, it *is* the high 16 bits... of a 48-bit address. I 
don't usually think of it that way, but you weren't that far off (just 
enough so it didn't work! :)

Some good info on segment registers here:

http://geezer.osdevbrasil.net/johnfine/segments.htm

But...

> This is for the windows operating system.

I'm running Linux, and mostly programmed for DOS when I *was* running 
Windows, but I know it's a "flat memory model". Jerry explains it - base 
is zero and limit is 4G-1 for all segments (fs is an exception, and is 
used for "thread local storage" - non-zero base, and probably a smaller 
limit... I'm guessing at that). For most purposes, we can forget that 
segment registers exist... which is a blessing!

> Do you think this is the
> best approach to figuring out if the 'rep movs' will cause a memory
> access exception, or is there a better way of doing it? I know the
> destination is valid, but not the source.

Well... you may not need to. If you were copying from some "random" 
address - copy [123] to [my_new_buffer] - it might (would!) fail, but 
where would source "come from" if it weren't valid memory? Probably wort 
checking to make sure it isn't zero - and that "size" isn't zero.

In Linux (won't help you, but there may be something similar) there's a 
kernel service "sys_access". This is intended to check read/write/exec 
access to a *file*, not memory. But it has the interesting property that 
if the address for "filename" is "off in the weeds", it returns -EINVAL 
rather than crashing with an access violation. I got the idea that this 
could be "abused" for... pretty much what you have in mind. Pass it an 
arbitrary address for "filename" ("path", actually)... we don't expect 
it to succeed - there's no filename there - but if it fails with 
-EINVAL, we know the memory isn't valid. This worked, but - in *this* 
particular kernel (I'm way out-of-date!), and in an "xterm", not a 
"console", it behaved as if sys_access was *making* the memory valid. 
Very strange! I didn't look into it very deeply, 'cause there's rarely a 
need to be using an "arbitrary address". But there may be something like 
this that you could do in Windows(?).

You might be able to make your routine go faster by moving a byte at a 
time until you get to a dword aligned address (if it isn't already), and 
then moving four (or more?) bytes at a time. Agner Fog, an optimization 
expert, offers some code:

http://www.agner.org/optimize/#asmlib

Might be worth looking to see how he did it. But that's a different 
issue. I encourage you to write your own routine, but I suspect you'll 
find the memory copy provided by the system hard to beat.

> 'fclex' was in there by accident. I just added that as a test to try
> and make it ignore access violations from the previous instruciton.

Mmmm. I'm pretty sure "fclex" would only clear floating point 
exceptions, and wouldn't help with access violations - I think what 
you'd get would be a "page fault". I like your thinking, though!

Best,
Frank
0
Reply Frank 8/2/2009 9:12:14 AM


Geoff asked:
[...]
> My problem is in the bound checking and my use of jnz and verr.
> Anyone know why it isn't working?
> According to the documentation I thought this would work.

VERR,rm16    ;sets ZF if 'selector' is readable
             ;affects no other flags
             ;an illegal instruction in real-mode !

ZF:
je/jz       ;jump if ZF=1
jne/jnz     ;jump id ZF=0

AC-flag     ;Aux-Carry aka (4-bit)Nibble-Carry, is rare usable
            ;due to missing support (not involved in Jcc/...)
            ;it affects only BCD opcodes like AAA,AAD,DAA,...

windoze:
CS,DS,ES,SS -segments are flat (FS,GS shouldn't be touched in user mode).
An access check can be done by 'try and fail', which needs to hook
the debug function of the OS.
I don't know details on windoze, I'd search the API for exception/debug.

__
wolfgang



0
Reply Wolfgang 8/2/2009 10:26:11 AM

Thanks a lot for all the help, I found a solution which seems to work
perfectly. As you guys suggested I looked at the kernel functions and
found the api function 'IsBadReadPtr':
http://msdn.microsoft.com/en-us/library/aa366713(VS.85).aspx

Here is the simplified fixed code:

push <copySize> // Set size parameter
push <souceAddress> // Set address parameter
call <offset.DLL.kernel32.IsBadReadPtr>
cmp eax, 0x0
jne <label3>

// Perform the copy of <freeCopySize> bytes
mov esi, <souceAddress> // source address
mov edi, <destAddress> // destination address
mov ecx, <copySize> // number of byte to copy
rep movs byte [edi], byte [esi]
<label3:>

Frank wrote:
>>Well... you may not need to. If you were copying from some "random"
>>address - copy [123] to [my_new_buffer] - it might (would!) fail, but
>>where would source "come from" if it weren't valid memory? Probably wort
>>checking to make sure it isn't zero - and that "size" isn't zero.

Unfortunately this is a weird situation. This code is being added to
the RtlFreeHeap(IN PVOID  HeapHandle,IN ULONG  Flags,IN PVOID
HeapBase) function in nt.dll, and it only has information to the
location of the memory, but not the size. I could make it communicate
with my RtlAllocHeap instrumentation to lookup the size, but for me
the code would be too painful to hand-write in assembly because I
would need a hash table. So instead I am just copying 'X' number of
bytes from each address, hoping hoping the copy doesn't extend past
the end of the heap it is allocated in.

Jerry wrote:
>>Inside of Windows, you'll almost never see anything but a 32-bit
>>address. The basic setup is that CS, DS, ES and SS are all set up
>>with a base of 0 and a limit of 4 Gig. Inside of that address space,
>>only paging is used to determine what's readable, writable, etc.

I see. So verr was really the completely wrong thing to be trying to
use.

Thanks everyone for your help, I really learned a lot. I will
definitely be reading a lot of articles in this group.

Geoff
0
Reply Geoff 8/2/2009 9:18:52 PM

On Aug 2, 2:18=A0pm, Geoff <glmcd...@munged.microcosmotalk.com> wrote:
> Thanks a lot for all the help, I found a solution which seems to work
> perfectly. As you guys suggested I looked at the kernel functions and
> found the api function 'IsBadReadPtr':http://msdn.microsoft.com/en-us/lib=
rary/aa366713(VS.85).aspx

Make sure to read that page in its entirety and pay attention to
http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx
(cited there as well).

Alex
0
Reply Alexei 8/2/2009 11:08:12 PM

On Aug 2, 5:08=A0pm, "Alexei A. Frounze"
<alexfrun...@MUNGED.microcosmotalk.com> wrote:
> On Aug 2, 2:18=3DA0pm, Geoff <glmcd...@munged.microcosmotalk.com> wrote:>=
 Thanks a lot for all the help, I found a solution which seems to work
> > perfectly. As you guys suggested I looked at the kernel functions and
> > found the api function 'IsBadReadPtr':http://msdn.microsoft.com/en-us/l=
ib=3D
>
> rary/aa366713(VS.85).aspx
>
> Make sure to read that page in its entirety and pay attention tohttp://bl=
ogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx
> (cited there as well).
>
> Alex

I took a look the warnings before I implemented this. This is just a
hobby project and it doesn't need to be perfect. It seems this is the
best solution without creating a hashtable and looking up the correct
sizes and adding hooks into more functions such as HeapDestroy,
ReAlloc, and others. So far I have tried the tool on multiple programs
and haven't been able to get my code injections to crash the
applications, which is a good sign.

Thanks again for your help!

0
Reply Geoff 8/5/2009 5:53:03 PM

11 Replies
68 Views

(page loaded in 0.094 seconds)


Reply: