CodeGuru Forums -
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic Newsletters VB Forums Developer.com


Newest CodeGuru.com Articles:

  • Installing SQL Server 2008
  • Writing UDFs for Firebird Embedded SQL Server
  • [Updated] Shutdown Manager
  • Building Windows Azure Cloud Service Applications with Azure Storage and the Azure SDK

  • Search CodeGuru:
     



    Go Back   CodeGuru Forums > Other Programming > Assembly
    FAQ Members List Calendar Search Today's Posts Mark Forums Read

    Assembly Questions and Answers for Assembly here!

    Reply
     
    Thread Tools Search this Thread Rate Thread Display Modes
      #1    
    Old September 4th, 2004, 10:05 AM
    Bornish's Avatar
    Bornish Bornish is offline
    Senior Member
     
    Join Date: Aug 2004
    Location: Bucharest, Romania... sometimes
    Posts: 1,031
    Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)
    dumping stack for all threads

    Aloha!
    I'm in a bit of a jam... any hints will be greatly appreciated.
    Description:
    There's this multithreaded process which throws an exception. In the exception handler, am trying to dump the stack for all the threads in the process. For the thread generating the exception is a piece of cake, and I figured will be almost as easy for the other threads:
    Step 1 - get the context of each thread using GetThreadContext() API
    Step 2 - get TIB (Thread Information Block) of the thread in question
    Step 3 - dump from stack's top until stack's base
    Unexpected result:
    I believed that when switching the thread's context for yielding the execution to a different thread, SegFs member of the CONTEXT structure must have a different value from one thread to another, in order to have FS:[18h] pointing to a different TIB structure. It seems I was wrong!
    Questions:
    - Is the following function retrieving the TIB of the calling thread?
    Code:
    	static tagXTIB* GetTIB()
    	{
    		tagXTIB* pTib;
    		__asm
    		{
    			MOV EAX , FS:[18h]
    			MOV pTib , EAX
    		}
    		return pTib;
    	}
    ...where tagXTIB is defined as:
    Code:
    typedef struct tagXTIB
    {
    	PEXCEPTION_REGISTRATION_RECORD pvExcept; //00h Head of exception record list
    	PVOID pvStackUserTop; //04 Top of user stack
    	PVOID pvStackUserBase; //08h Base of user stack
    	union
    	{
    		struct //Win95 fields
    		{
    			WORD pvTDB; //0Ch
    			WORD pvThunkSS; //0Eh SS selector used for thunking to 16 bits
    			DWORD unknown1; //10h
    		} WIN95;
    		struct //WinNT fields
    		{
    			PVOID SubSystemTib; //0Ch
    			ULONG FiberData; //10h
    		} WINNT;
    	} TIB_UNION1;
    	PVOID pvArbitrary; //14h Available for application
    	struct _tib *ptibSelf; //18h Linear address of TIB structure
    	union
    	{
    		struct //Win95 fields
    		{
    			WORD TIBFlags; //1Ch
    			WORD Win16MutexCount; //1Eh
    			DWORD DebugContext; //20h
    			DWORD pCurrentPriority; //24h
    			DWORD pvQueue; //28h Message Queue
    		} WIN95;
    		struct //WinNT fields
    		{
    			DWORD unknown1; //1Ch
    			DWORD processID; //20h
    			DWORD threadID; //24h
    			DWORD unknown2; //28h
    		} WINNT;
    	} TIB_UNION2;
    	PVOID* pvTLSArray;
    	union
    	{
    		struct //Win95 fields
    		{
    			PVOID* pProcess; //30h Pointer to owning process database
    		} WIN95;
    	} TIB_UNION3;
    } XTIB;
    #pragma pack()
    - Are the pvStackUserTop & pvStackUserBase members of tagXTIB structure the addresses defining the memory interval where the thread's stack is located?

    - Do you know any other way of getting these values, besides reading them from the TIB?

    - In order to get the address of the TIB for all the other threads of the process (not the calling thread), is it correct to use the SegFs member of the CONTEXT structure filled by a GetThreadContext() call, instead of FS from GetTIB() function?

    - Then, why SegFs is equal with FS? Makes no sense to get the TIBs this way, since FS:[18h] will be equal with SegFs:[18h], but TIB should be different for two threads, isn't it?

    Conclusions:
    My interest is to dump the stack for each thread belonging to the process. Having the stack's top & base addresses, and also the Esp pointer (which seems to be retrieved correctly by the API), I will be able to list the call stack. All registers are looking as valid information, as returned by GetThreadContext() API. I've checked Eip value, too. So, makes me believe that SegFs is also correct. But then, why has the same value with the calling thread's FS? If so, from where to read the TIB of the other threads? Or, at least, how to get the correct location of their stack (because I know for sure that threads are not sharing the stack)?

    Many anticipated thanks,
    __________________
    Bogdan Apostol
    ESRI Developer Network

    Compilers demystified - Function pointers in Visual Basic 6.0
    Enables the use of function pointers in VB6 and shows how to embed native code in a VB application.

    Customize your R2H
    The unofficial board dedicated to ASUS R2H UMPC owners.
    Reply With Quote
      #2    
    Old July 9th, 2006, 09:26 AM
    Krishnaa's Avatar
    Krishnaa Krishnaa is offline
    Microsoft MVP
    Power Poster
     
    Join Date: Aug 1999
    Location: <Classified>
    Posts: 6,747
    Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)
    Re: dumping stack for all threads

    I am not sure if your question is answered already, so excuse me if I am posting in old thread.

    Here is a good article from Matt Patriek on TIBs, Very similar to what you are trying to do,

    MSJ Article on TIBs
    http://www.microsoft.com/msj/archive/S2CE.aspx

    He also had the sample code (SHOWTIB), see DisplayTIB function, not present in source code downloads (at least I couldnt find it there)
    So here is a copy I got from somewhere on web,
    Code:
    tib.h and showtib.cpp
    
    TIB.H
     //===========================================================
    // File: TIB.H
    // Author: Matt Pietrek
    // From: Microsoft Systems Journal "Under the Hood", May 1996
    //===========================================================
    #pragma pack(1)
    
    typedef struct _EXCEPTION_REGISTRATION_RECORD
    {
        struct _EXCEPTION_REGISTRATION_RECORD * pNext;
        FARPROC                                 pfnHandler;
    } EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
    
    typedef struct _TIB
    {
    PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list
    PVOID   pvStackUserTop;     // 04h Top of user stack
    PVOID   pvStackUserBase;    // 08h Base of user stack
    
    union                       // 0Ch (NT/Win95 differences)
    {
        struct  // Win95 fields
        {
            WORD    pvTDB;         // 0Ch TDB
            WORD    pvThunkSS;     // 0Eh SS selector used for thunking to 16 bits
            DWORD   unknown1;      // 10h
        } WIN95;
    
        struct  // WinNT fields
        {
            PVOID SubSystemTib;     // 0Ch
            ULONG FiberData;        // 10h
        } WINNT;
    } TIB_UNION1;
    
    PVOID   pvArbitrary;        // 14h Available for application use
    struct _tib *ptibSelf;      // 18h Linear address of TIB structure
    
    union                       // 1Ch (NT/Win95 differences)
    {
        struct  // Win95 fields
        {
            WORD    TIBFlags;           // 1Ch
            WORD    Win16MutexCount;    // 1Eh
            DWORD   DebugContext;       // 20h
            DWORD   pCurrentPriority;   // 24h
            DWORD   pvQueue;            // 28h Message Queue selector
        } WIN95;
    
        struct  // WinNT fields
        {
            DWORD unknown1;             // 1Ch
            DWORD processID;            // 20h
            DWORD threadID;             // 24h
            DWORD unknown2;             // 28h
        } WINNT;
    } TIB_UNION2;
    
    PVOID*  pvTLSArray;         // 2Ch Thread Local Storage array
    
    union                       // 30h (NT/Win95 differences)
    {
        struct  // Win95 fields
        {
            PVOID*  pProcess;     // 30h Pointer to owning process database
        } WIN95;
    } TIB_UNION3;
        
    } TIB, *PTIB;
    #pragma pack()
    
    
    SHOWTIB.CPP
     //==========================================================================
    // File: SHOWTIB.CPP
    // Author: Matt Pietrek
    // To Build:
    //  CL /MT SHOWTIB.CPP USER32.LIB (Visual C++)
    //  BCC32 -tWM SHOWTIB.CPP (Borland C++, TASM32 required)
    //==========================================================================
    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <process.h>
    #pragma hdrstop
    #include "tib.h"
    
    #define SHOWTIB_MAX_THREADS 64
    
    CRITICAL_SECTION gDisplayTIB_CritSect;
    
    void DisplayTIB( PSTR pszThreadName )
    {
        PTIB pTIB;
        WORD fsSel;
    
        EnterCriticalSection( &gDisplayTIB_CritSect );
    
        __asm
        {
            mov     EAX, FS:[18h]
            mov     [pTIB], EAX
            mov     [fsSel], FS
        }
            
        printf( "Contents of thread %s\n", pszThreadName );
        
        printf( "  TIB %04X (Address: %08X)\n", fsSel, pTIB );
        printf( "  SEH chain: %08X\n", pTIB->pvExcept );
        printf( "  Stack top: %08X\n", pTIB->pvStackUserTop );
        printf( "  Stack base: %08X\n", pTIB->pvStackUserBase );
        printf( "  pvArbitray: %08X\n", pTIB->pvArbitrary );
        printf( "  TLS array *: %08X\n", pTIB->pvTLSArray );
    
        printf( "  ----OS Specific fields----\n" );
        if ( 0xC0000000 == (GetVersion() & 0xC0000000) )    // Is this Win95 ?
        {
            printf( "  TDB: %04X\n", pTIB->TIB_UNION1.WIN95.pvTDB );
            printf( "  Thunk SS: %04X\n", pTIB->TIB_UNION1.WIN95.pvThunkSS );
            printf( "  TIB flags: %04X\n", pTIB->TIB_UNION2.WIN95.TIBFlags );
            printf( "  Win16Mutex count: %04X\n",
                        pTI->TIB_UNION2.WIN95.Win16MutexCount );
            printf( "  DebugContext: %08X\n", pTIB->TIB_UNION2.WIN95.DebugContext );
            printf( "  Current Priority *: %08X (%u)\n",
                        pTIB->TIB_UNION2.WIN95.pCurrentPriority,
                        *(PDWORD)(pTIB->TIB_UNION2.WIN95.pCurrentPriority) );
            printf( "  Queue: %04X\n", pTIB->TIB_UNION2.WIN95.pvQueue );
            printf( "  Process *: %08X\n", pTIB->TIB_UNION3.WIN95.pProcess );
        }
        else if ( 0 == (GetVersion() & 0xC0000000) )    // Is this WinNT?
        {
            printf("  SubSystem TIB: %08X\n", pTIB->TIB_UNION1.WINNT.SubSystemTib);
            printf("  FiberData: %08X\n", pTIB->TIB_UNION1.WINNT.FiberData );
            printf("  unknown1: %08X\n", pTIB->TIB_UNION2.WINNT.unknown1);
            printf("  process ID: %08X\n", pTIB->TIB_UNION2.WINNT.processID);
            printf("  thread ID: %08X\n", pTIB->TIB_UNION2.WINNT.threadID);
            printf("  unknown2: %08X\n", pTIB->TIB_UNION2.WINNT.unknown2);
        }
        else
        {
            printf("  Unsupported Win32 implementation\n" );
        }
        
        printf( "\n" );
    
        LeaveCriticalSection( &gDisplayTIB_CritSect );
    }
    
    void MyThreadFunction( void * threadParam )
    {
        char szThreadName[128];
    
        wsprintf( szThreadName, "%u", threadParam );    // Give the thread a name
    
        // If multiple threads are specified, give'em different priorities
        if ( (DWORD)threadParam & 1 )
            SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
    
        DisplayTIB( szThreadName );     // Display the thread's TIB
    
        // Let other threads execute while this thread is still alive.  The idea
        // here is to try and prevent memory region and selector reuse.
        Sleep( 1000 );
    }
    
    int main( int argc, char *argv[] )
    {
        if ( argc < 2 )
        {
            printf( "Syntax: SHOWTIB [# of threads]\n" );
            return 1;
        }
    
        InitializeCriticalSection( &gDisplayTIB_CritSect );
        
        unsigned cThreads = atoi( argv[1] );
    
        if ( (cThreads < 1) || (cThreads > SHOWTIB_MAX_THREADS) )
        {
            printf( "thread count must be > 1 and < %u\n", SHOWTIB_MAX_THREADS );
        }
        else
        {
            // Allocate an array to hold the thread handles
            HANDLE threadHandles[ SHOWTIB_MAX_THREADS ];
    
            // Create the specified number of threads
            for ( unsigned i = 0; i < cThreads; i++ )
                threadHandles[i] = (HANDLE)
                    _beginthread(MyThreadFunction,0,(PVOID)i);
    
            // Wait for all the threads to finish before we exit the program
            WaitForMultipleObjects( cThreads, threadHandles, TRUE, INFINITE );
    
            // We don't need the thread handles anymore.  Close'em!
            for ( i = 0; i < cThreads; i++ )
                CloseHandle( threadHandles[i] );
        }
        
        DeleteCriticalSection( &gDisplayTIB_CritSect );
    
        return 0;
    }
    __________________
    Regards,
    Ramkrishna Pawar
    Reply With Quote
      #3    
    Old July 10th, 2006, 05:11 AM
    Bornish's Avatar
    Bornish Bornish is offline
    Senior Member
     
    Join Date: Aug 2004
    Location: Bucharest, Romania... sometimes
    Posts: 1,031
    Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)
    Re: dumping stack for all threads

    Thanks Krishnaa for your reply! I appreciate your effort of gathering some information about TIBs, but unfortunatelly does not answer my question. Your sample code displays the TIB of each thread from within the threads themselves. What I am looking for is to read from a thread "A" the TIB of a thread "B". I assume it is possible to obtain the address of the TIB of each thread in the process when an exception occurs because most debuggers can.
    This is my purpose: to install a "global" hook for a process which catches all exceptions raised by all its threads, and log some info about the state of all threads (not only the one that raised the exception). I have managed to implement everything else (including exe patching used to debug applications which can't be recompiled) but currently my log contains only info about the thread that crashed (i do log only unhandled exceptions, not handled ones) and sometimes that isn't enough to catch a bug.
    I have also implemented a function call stack retrieval algorithm that works also in some cases of corrupted stack (a bit better than the one in VS 6.0, and similar to the one in VS 7.1). When exceptions are not handled, my implementation uses some heuristics, trying to skip the bad code and resume execution, which in many cases helps an application to regain control and let the user save data,... etc.
    Before I've posted here I have read most of the related articles (also from MSJ) I could find and tested many sample source code, but unfortunatelly I couldn't find an answer until today, after more than 2 years since it all started.

    Regards,
    __________________
    Bogdan Apostol
    ESRI Developer Network

    Compilers demystified - Function pointers in Visual Basic 6.0
    Enables the use of function pointers in VB6 and shows how to embed native code in a VB application.

    Customize your R2H
    The unofficial board dedicated to ASUS R2H UMPC owners.
    Reply With Quote
      #4    
    Old July 10th, 2006, 05:54 AM
    Krishnaa's Avatar
    Krishnaa Krishnaa is offline
    Microsoft MVP
    Power Poster
     
    Join Date: Aug 1999
    Location: <Classified>
    Posts: 6,747
    Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)
    Re: dumping stack for all threads

    Ohh...

    Are those other threads suspended before using GetThreadContext() ?
    __________________
    Regards,
    Ramkrishna Pawar
    Reply With Quote
      #5    
    Old July 10th, 2006, 06:20 AM
    Bornish's Avatar
    Bornish Bornish is offline
    Senior Member
     
    Join Date: Aug 2004
    Location: Bucharest, Romania... sometimes
    Posts: 1,031
    Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)
    Re: dumping stack for all threads

    Quote:
    Originally Posted by Krishnaa
    Ohh...

    Are those other threads suspended before using GetThreadContext() ?
    Yes, but FS register contains the address of the TIB for the calling thread, so:
    Code:
        __asm
        {
            mov     EAX, FS:[18h]
            mov     [pTIB], EAX
            mov     [fsSel], FS
        }
    will not help to obtain the address of the TIB of another thread. In fact, I would be happy enough to get the "top" and "base" address for the stack of each thread when the unhandled exception occurs. Yes, all threads are suspended and other requirements met, but I need to figure out how context switching occurs. Do I have to execute code in Ring 0 to be able to access this kind of information? I don't even know where to start looking into this matter. uf...
    __________________
    Bogdan Apostol
    ESRI Developer Network

    Compilers demystified - Function pointers in Visual Basic 6.0
    Enables the use of function pointers in VB6 and shows how to embed native code in a VB application.

    Customize your R2H
    The unofficial board dedicated to ASUS R2H UMPC owners.
    Reply With Quote
      #6    
    Old July 10th, 2006, 06:32 AM
    Krishnaa's Avatar
    Krishnaa Krishnaa is offline
    Microsoft MVP
    Power Poster
     
    Join Date: Aug 1999
    Location: <Classified>
    Posts: 6,747
    Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)
    Re: dumping stack for all threads

    I am not sure of TIB information, but here is one StackWalker written by Jochen Kalmbach [MVP VC++], It can dump the stack for any thread, of any process.

    http://www.codeproject.com/threads/StackWalker.asp

    This can at least meet your requirement of dumping the call stack of all threads.
    __________________
    Regards,
    Ramkrishna Pawar
    Reply With Quote
      #7    
    Old July 10th, 2006, 07:36 AM
    Bornish's Avatar
    Bornish Bornish is offline
    Senior Member
     
    Join Date: Aug 2004
    Location: Bucharest, Romania... sometimes
    Posts: 1,031
    Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)
    Re: dumping stack for all threads

    Quote:
    Originally Posted by Krishnaa
    I am not sure of TIB information, but here is one StackWalker written by Jochen Kalmbach [MVP VC++], It can dump the stack for any thread, of any process.

    http://www.codeproject.com/threads/StackWalker.asp

    This can at least meet your requirement of dumping the call stack of all threads.
    Thanks for finding this article for me. The source code is quite well documented, and hope it will work with multiple threads (didn't have time yet to test it in a multithreaded app). I recall having GetThreadContext working just fine for most registers (eax, ecx,...) but returning the same value for FS register of different threads, which confused me in the way TIB are retrieved. I'll go through the StackWalker and figure out what is it doing to get the info about all threads of a process.
    Thanks for all your help,
    __________________
    Bogdan Apostol
    ESRI Developer Network

    Compilers demystified - Function pointers in Visual Basic 6.0
    Enables the use of function pointers in VB6 and shows how to embed native code in a VB application.

    Customize your R2H
    The unofficial board dedicated to ASUS R2H UMPC owners.
    Reply With Quote
      #8    
    Old July 10th, 2006, 07:41 AM
    Krishnaa's Avatar
    Krishnaa Krishnaa is offline
    Microsoft MVP
    Power Poster
     
    Join Date: Aug 1999
    Location: <Classified>
    Posts: 6,747
    Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)Krishnaa has a reputation beyond repute (3000+)
    Re: dumping stack for all threads

    I have tested it, by uncommenting the TestDifferentThread() call in _tmain() function. It works pretty well.
    __________________
    Regards,
    Ramkrishna Pawar
    Reply With Quote
      #9    
    Old July 10th, 2006, 08:43 AM
    Bornish's Avatar
    Bornish Bornish is offline
    Senior Member
     
    Join Date: Aug 2004
    Location: Bucharest, Romania... sometimes
    Posts: 1,031
    Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)Bornish is a splendid one to behold (750+)
    Re: dumping stack for all threads

    Quote:
    Originally Posted by Krishnaa
    I have tested it, by uncommenting the TestDifferentThread() call in _tmain() function. It works pretty well.
    Thanks, I will have a look as soon as I get the time. I still wonder why the SegFS returned by GetThreadContext is the same as FS register of the calling thread, for a given handle of a different thread.
    __________________
    Bogdan Apostol
    ESRI Developer Network

    Compilers demystified - Function pointers in Visual Basic 6.0
    Enables the use of function pointers in VB6 and shows how to embed native code in a VB application.

    Customize your R2H
    The unofficial board dedicated to ASUS R2H UMPC owners.
    Reply With Quote
    Reply

    Bookmarks
    Go Back   CodeGuru Forums > Other Programming > Assembly


    Thread Tools Search this Thread
    Search this Thread:

    Advanced Search
    Display Modes Rate This Thread
    Rate This Thread:

    Posting Rules
    You may not post new threads
    You may not post replies
    You may not post attachments
    You may not edit your posts

    BB code is On
    Smilies are On
    [IMG] code is On
    HTML code is Off
    Forum Jump


    All times are GMT -5. The time now is 02:42 PM.



    Acceptable Use Policy

    internet.comMediabistrojusttechjobs.comGraphics.com

    WebMediaBrands Corporate Info


    Advertise | Newsletters | Feedback | Submit News

    Legal Notices | Licensing | Permissions | Privacy Policy


    Powered by vBulletin® Version 3.7.3
    Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
    Copyright WebMediaBrands Inc. 2002-2009