Page 1 of 1

msvcrt.dll _flsbuf (int ch, FILE*str) Bug?

Posted: Fri Feb 16, 2007 1:21 am
by flash
HI! Everyone.
These are some code I disassemble from msvcrt.dll(Windows XP2).

_flsbuf()

77C0ED01 cmp ebx, FFFFFFFF
77C0ED04 je 77C0ED1F
77C0ED06 mov ecx, ebx
77C0ED08 sar ecx, 05
77C0ED0B mov ecx, dword ptr [4*ecx+77C32440]
77C0ED12 mov eax, ebx
77C0ED14 and eax, 0000001F
77C0ED17 lea eax, dword ptr[eax+8*eax]

77C0ED1A lea eax, dword ptr[ecx+4*eax]
77C0ED1D jmp 77C0ED24
77C0ED1F mov eax, 77C2F310
77C0ED24 test [eax+04], 20
77C0ED28 je 77C0ED37
77C0ED2A push 00000002
77C0ED2C push 00000000
77C0ED2E push ebx
77C0ED2F call 77BFEFB0 /* call _lseek( ebx, 0, SEEK_END ) */
...


Please look at the red line instruction, after
"and eax, 0000001F"
the eax is less than 01Fh, right?
And ,
"dword ptr[eax+8*eax]",
the pointer is the value of "eax+8*eax",
and the result is much less than 4MB,
so I think the program reached the invalid memory.
I found this problem while I was debugging msvcrt.dll with my os. An pagefault occured to tell me that.



This is the source code of _flsbuf.c

Code: Select all

/***
*_flsbuf.c - flush buffer and output character.
*
*       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
*       defines _flsbuf() - flush a file buffer and output a character.
*       If no buffer, make one.
*
*******************************************************************************/
#include <cruntime.h>
#include <stdio.h>
#include <file2.h>
#include <io.h>
#include <dbgint.h>
#include <malloc.h>
#include <msdos.h>
#include <wchar.h>
#include <internal.h>
#ifdef _MT
#include <mtdll.h>
#endif  /* _MT */
#include <tchar.h>

/***
*int _flsbuf(ch, stream) - flush buffer and output character.
*
*Purpose:
*       flush a buffer if this stream has one. if not, try to get one. put the
*       next output char (ch) into the buffer (or output it immediately if this
*       stream can't have a buffer). called only from putc. intended for use
*       only within library.
*
*       [NOTE: Multi-thread - It is assumed that the caller has aquired
*       the stream lock.]
*
*Entry:
*       FILE *stream - stream to flish and write on
*       int ch - character to output.
*
*Exit:
*       returns -1 if FILE is actually a string, or if can't write ch to
*       unbuffered file, or if we flush a buffer but the number of chars
*       written doesn't agree with buffer size.  Otherwise returns ch.
*       all fields in FILE struct can be affected except _file.
*
*Exceptions:
*
*******************************************************************************/

int __cdecl _flsbuf (
        int ch,
        FILE *str
        )
{

        REG1 FILE *stream;
        REG2 int charcount;
        REG3 int written;
        int fh;

        _ASSERTE(str != NULL);

        /* Init file handle and pointers */
        stream = str;
        fh = _fileno(stream);

        if (!(stream->_flag & (_IOWRT|_IORW)) || (stream->_flag & _IOSTRG)) {
                stream->_flag |= _IOERR;
                return(_TEOF);
        }

        if (stream->_flag & _IOREAD) {
                stream->_cnt = 0;
                if (stream->_flag & _IOEOF) {
                        stream->_ptr = stream->_base;
                        stream->_flag &= ~_IOREAD;
                }
                else {
                        stream->_flag |= _IOERR;
                        return(_TEOF);
                }
        }

        stream->_flag |= _IOWRT;
        stream->_flag &= ~_IOEOF;
        written = charcount = stream->_cnt = 0;

        /* Get a buffer for this stream, if necessary. */
        if (!anybuf(stream)) {
                if (!( ((stream==stdout) || (stream==stderr))
                && (_isatty(fh)) ))

                        _getbuf(stream);

        } /* end !anybuf() */

        /* If big buffer is assigned to stream... */
        if (bigbuf(stream)) {

                _ASSERTE(("inconsistent IOB fields", stream->_ptr - stream->_base >= 0));
                charcount = stream->_ptr - stream->_base;
                stream->_ptr = stream->_base + sizeof(TCHAR);
                stream->_cnt = stream->_bufsiz - sizeof(TCHAR);

                if (charcount > 0)
                        written = _write(fh, stream->_base, charcount);
                else
                       if (_osfile_safe(fh) & FAPPEND)       **** failed here****
                                _lseek(fh,0L,SEEK_END);       
              *stream->_base = (char)ch;
        }
    /* Perform single character output (either _IONBF or no buffering) */
        else {
                charcount = sizeof(TCHAR);
                written = _write(fh, &ch, charcount);
        }

        /* See if the _write() was successful. */
        if (written != charcount) {
                stream->_flag |= _IOERR;
                return(_TEOF);
        }
        return(ch & 0xff);
}


Posted: Fri Feb 16, 2007 3:55 am
by Combuster
eeh, a lea instruction doesnt access memory. What the compiler did here was some advanced asm optimisation:

lea eax, [eax+8*eax]
lea eax, [ecx+4*eax]
=
mov eax, eax + 8 * eax
mov eax, ecx + 4 * eax
=
mov eax, 9 * eax
mov eax, ecx + 4 * eax
=
mov eax, ecx + 4 * 9 * eax
=
mov eax, ecx + 36 * eax

the next meaningful instruction is a test on this piece of memory: i dont see a page fault occurring here

if you have checked, ecx is some pointer which is taken from memory, and by the looks of it, its an array with elements of 36 bytes in size. (you could partially reverse engineer it as if(structure[eax & 0x1f] & 0x20) { ... }

I cant find the piece of source that goes with it, however (maybe some macro expansion?)

Posted: Fri Feb 16, 2007 4:50 am
by flash
Yes.

The pagefault occured at:

77C0ED24 test [eax+04], 20


Does it access the invalid memory???

Posted: Fri Feb 16, 2007 4:55 am
by Combuster
most likely, it does
you should however take a look at memory location 77C32440 (where ecx is taken from) to see if it contains valid pointers

Posted: Fri Feb 16, 2007 5:11 am
by flash
Thanks very much!!

After I checked.

I found eax is 72 and the ecx is 0.

I think the file handle was passed a value NULL..

Posted: Fri Feb 16, 2007 11:10 am
by Brynet-Inc
Uh? Is this code open source?
While I'm definitely not against reverse engineering..
But if this is copyrighted closed source code..
Should it be posted & discussed about on this forum?..

:?

Posted: Fri Feb 16, 2007 3:49 pm
by niteice
Microsoft distributes their libc source with several editions of VC++.

Posted: Sat Feb 17, 2007 4:36 am
by flash
Yes. Mine is Visual C++ 6 Standard Edition.

The CRT source codes are in the installation cdrom.