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)
|