PDA

View Full Version : can anyone tell me why code such as this avoids access violation on write..


BanMe
December 31st, 2010, 04:33
The write is to USER_SHARED_SPACE I believe..
Code:


7C812DBB . 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C812DC1 . 8B70 30 MOV ESI,DWORD PTR DS:[EAX+30]
7C812DC4 . 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C812DCA . 8945 E0 MOV DWORD PTR SS:[EBP-20],EAX
7C812DCD . FF15 BC10807C CALL DWORD PTR DS:[<&ntdll.RtlAcquirePeb>; ntdll.RtlAcquirePebLock
7C812DD3 . 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
7C812DD7 . 6A 00 PUSH 0
7C812DD9 . 6A 01 PUSH 1
7C812DDB . FF76 40 PUSH DWORD PTR DS:[ESI+40]
7C812DDE . 8B3D 1815807C MOV EDI,DWORD PTR DS:[<&ntdll.RtlFindCle>; ntdll.RtlFindClearBitsAndSet
7C812DE4 . FFD7 CALL EDI ; ntdll.7C910228; <&ntdll.RtlFindClearBitsAndSet>
7C812DE6 . 8BD8 MOV EBX,EAX
7C812DE8 . 895D E4 MOV DWORD PTR SS:[EBP-1C],EBX
7C812DEB . 83FB FF CMP EBX,-1
7C812DEE . 0F84 8A1A0300 JE kernel32.7C84487E
7C812DF4 . 8B45 E0 MOV EAX,DWORD PTR SS:[EBP-20]
7C812DF7 . 83A498 100E000>AND DWORD PTR DS:[EAX+EBX*4+E10],0
7C812DFF > 834D FC FF OR DWORD PTR SS:[EBP-4],FFFFFFFF
7C812E03 . E8 19000000 CALL kernel32.7C812E21


7ffde000+e10 + index 1 * 4 ...so what checks the TlsBitmap...in order to avoid this occurance o0.

Indy
December 31st, 2010, 07:28
BanMe
7ffde000 - TEB(applied ASLR, R/W). USER_SHARED_DATA this is the last page in address space, ie address 7ffe0000(R/E).

Code:
7C812DC4 . 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7C812DCA . 8945 E0 MOV DWORD PTR SS:[EBP-20],EAX

This is a reference to itself, ie TEB.Tib.Self

Offset 0xE10 - this field TlsExpansionSlots in TEB.

Code:
DWORD
APIENTRY
TlsAlloc(
VOID
)

/*++

Routine Description:

A TLS index may be allocated using TlsAlloc. Win32 garuntees a
minimum number of TLS indexes are available in each process. The
constant TLS_MINIMUM_AVAILABLE defines the minimum number of
available indexes. This minimum is at least 64 for all Win32
systems.

Arguments:

None.

Return Value:

Not-0xffffffff - Returns a TLS index that may be used in a
subsequent call to TlsFree, TlsSetValue, or TlsGetValue. The
storage associated with the index is initialized to NULL.

0xffffffff - The operation failed. Extended error status is available
using GetLastError.


--*/

{
PPEB Peb;
PTEB Teb;
DWORD Index;

Peb = NtCurrentPeb();
Teb = NtCurrentTeb();

RtlAcquirePebLock();
try {

Index = RtlFindClearBitsAndSet((PRTL_BITMAP)Peb->TlsBitmap,1,0);
if ( Index == 0xffffffff ) {
Index = RtlFindClearBitsAndSet((PRTL_BITMAP)Peb->TlsExpansionBitmap,1,0);
if ( Index == 0xffffffff ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
}
else {
if ( !Teb->TlsExpansionSlots ) {
Teb->TlsExpansionSlots = RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( TMP_TAG ) | HEAP_ZERO_MEMORY,
TLS_EXPANSION_SLOTS * sizeof(PVOID)
);
if ( !Teb->TlsExpansionSlots ) {
RtlClearBits((PRTL_BITMAP)Peb->TlsExpansionBitmap,Index,1);
Index = 0xffffffff;
BaseSetLastNTError(STATUS_NO_MEMORY);
return Index;
}
}
Teb->TlsExpansionSlots[Index] = NULL;
Index += TLS_MINIMUM_AVAILABLE;
}
}
else {
Teb->TlsSlots[Index] = NULL;
}
}
finally {
RtlReleasePebLock();
}
#if DBG
Index |= TLS_MASK;
#endif
return Index;
}

LPVOID
APIENTRY
TlsGetValue(
DWORD dwTlsIndex
)

/*++

Routine Description:

This function is used to retrive the value in the TLS storage
associated with the specified index.

If the index is valid this function clears the value returned by
GetLastError(), and returns the value stored in the TLS slot
associated with the specified index. Otherwise a value of NULL is
returned with GetLastError updated appropriately.

It is expected, that DLLs will use TlsAlloc and TlsGetValue as
follows:

- Upon DLL initialization, a TLS index will be allocated using
TlsAlloc. The DLL will then allocate some dynamic storage and
store its address in the TLS slot using TlsSetValue. This
completes the per thread initialization for the initial thread
of the process. The TLS index is stored in instance data for
the DLL.

- Each time a new thread attaches to the DLL, the DLL will
allocate some dynamic storage and store its address in the TLS
slot using TlsSetValue. This completes the per thread
initialization for the new thread.

- Each time an initialized thread makes a DLL call requiring the
TLS, the DLL will call TlsGetValue to get the TLS data for the
thread.

Arguments:

dwTlsIndex - Supplies a TLS index allocated using TlsAlloc. The
index specifies which TLS slot is to be located. Translating a
TlsIndex does not prevent a TlsFree call from proceding.

Return Value:

NON-NULL - The function was successful. The value is the data stored
in the TLS slot associated with the specified index.

NULL - The operation failed, or the value associated with the
specified index was NULL. Extended error status is available
using GetLastError. If this returns non-zero, the index was
invalid.

--*/
{
PTEB Teb;
LPVOID *Slot;

#if DBG
// See if the Index passed in is from TlsAlloc or random goo...
ASSERTMSG( "BASEDLL: Invalid TlsIndex passed to TlsGetValue\n", (dwTlsIndex & TLS_MASK));
dwTlsIndex &= ~TLS_MASK;
#endif

Teb = NtCurrentTeb();

if ( dwTlsIndex < TLS_MINIMUM_AVAILABLE ) {
Slot = &Teb->TlsSlots[dwTlsIndex];
Teb->LastErrorValue = 0;
return *Slot;
}
else {
if ( dwTlsIndex >= TLS_MINIMUM_AVAILABLE+TLS_EXPANSION_SLOTS ) {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
else {
Teb->LastErrorValue = 0;
if ( Teb->TlsExpansionSlots ) {
return Teb->TlsExpansionSlots[dwTlsIndex-TLS_MINIMUM_AVAILABLE];
}
else {
return NULL;
}
}
}
}

BOOL
APIENTRY
TlsSetValue(
DWORD dwTlsIndex,
LPVOID lpTlsValue
)

/*++

Routine Description:

This function is used to store a value in the TLS storage associated
with the specified index.

If the index is valid this function stores the value and returns
TRUE. Otherwise a value of FALSE is returned.

It is expected, that DLLs will use TlsAlloc and TlsSetValue as
follows:

- Upon DLL initialization, a TLS index will be allocated using
TlsAlloc. The DLL will then allocate some dynamic storage and
store its address in the TLS slot using TlsSetValue. This
completes the per thread initialization for the initial thread
of the process. The TLS index is stored in instance data for
the DLL.

- Each time a new thread attaches to the DLL, the DLL will
allocate some dynamic storage and store its address in the TLS
slot using TlsSetValue. This completes the per thread
initialization for the new thread.

- Each time an initialized thread makes a DLL call requiring the
TLS, the DLL will call TlsGetValue to get the TLS data for the
thread.

Arguments:

dwTlsIndex - Supplies a TLS index allocated using TlsAlloc. The
index specifies which TLS slot is to be located. Translating a
TlsIndex does not prevent a TlsFree call from proceding.

lpTlsValue - Supplies the value to be stored in the TLS Slot.

Return Value:

TRUE - The function was successful. The value lpTlsValue was
stored.

FALSE - The operation failed. Extended error status is available
using GetLastError.

--*/

{
PTEB Teb;

#if DBG
// See if the Index passed in is from TlsAlloc or random goo...
ASSERTMSG( "BASEDLL: Invalid TlsIndex passed to TlsSetValue\n", (dwTlsIndex & TLS_MASK));
dwTlsIndex &= ~TLS_MASK;
#endif

Teb = NtCurrentTeb();

if ( dwTlsIndex >= TLS_MINIMUM_AVAILABLE ) {
dwTlsIndex -= TLS_MINIMUM_AVAILABLE;
if ( dwTlsIndex < TLS_EXPANSION_SLOTS ) {
if ( !Teb->TlsExpansionSlots ) {
RtlAcquirePebLock();
if ( !Teb->TlsExpansionSlots ) {
Teb->TlsExpansionSlots = RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( TMP_TAG ) | HEAP_ZERO_MEMORY,
TLS_EXPANSION_SLOTS * sizeof(PVOID)
);
if ( !Teb->TlsExpansionSlots ) {
RtlReleasePebLock();
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
}
RtlReleasePebLock();
}
Teb->TlsExpansionSlots[dwTlsIndex] = lpTlsValue;
}
else {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return FALSE;
}
}
else {
Teb->TlsSlots[dwTlsIndex] = lpTlsValue;
}
return TRUE;
}

BOOL
APIENTRY
TlsFree(
DWORD dwTlsIndex
)

/*++

Routine Description:

A valid TLS index may be free'd using TlsFree.

Arguments:

dwTlsIndex - Supplies a TLS index allocated using TlsAlloc. If the
index is a valid index, it is released by this call and is made
available for reuse. DLLs should be carefull to release any
per-thread data pointed to by all of their threads TLS slots
before calling this function. It is expected that DLLs will
only call this function (if at ALL) during their process detach
routine.

Return Value:

TRUE - The operation was successful. Calling TlsTranslateIndex with
this index will fail. TlsAlloc is free to reallocate this
index.

FALSE - The operation failed. Extended error status is available
using GetLastError.

--*/

{
PPEB Peb;
BOOLEAN ValidIndex;
PRTL_BITMAP TlsBitmap;
NTSTATUS Status;
DWORD Index2;

#if DBG
// See if the Index passed in is from TlsAlloc or random goo...
ASSERTMSG( "BASEDLL: Invalid TlsIndex passed to TlsFree\n", (dwTlsIndex & TLS_MASK));
dwTlsIndex &= ~TLS_MASK;
#endif

Peb = NtCurrentPeb();

RtlAcquirePebLock();
try {

if ( dwTlsIndex >= TLS_MINIMUM_AVAILABLE ) {
Index2 = dwTlsIndex - TLS_MINIMUM_AVAILABLE;
if ( Index2 >= TLS_EXPANSION_SLOTS ) {
ValidIndex = FALSE;
}
else {
TlsBitmap = (PRTL_BITMAP)Peb->TlsExpansionBitmap;
ValidIndex = RtlAreBitsSet(TlsBitmap,Index2,1);
}
}
else {
TlsBitmap = (PRTL_BITMAP)Peb->TlsBitmap;
Index2 = dwTlsIndex;
ValidIndex = RtlAreBitsSet(TlsBitmap,Index2,1);
}
if ( ValidIndex ) {

Status = NtSetInformationThread(
NtCurrentThread(),
ThreadZeroTlsCell,
&dwTlsIndex,
sizeof(dwTlsIndex)
);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return FALSE;
}

RtlClearBits(TlsBitmap,Index2,1);
}
else {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
}
}
finally {
RtlReleasePebLock();
}
return ValidIndex;
}

BanMe
December 31st, 2010, 14:30
No, my description was inaccurate and my insanity got to me that late at night.

What I meant in my ramblings, I set the _tls_data DWORD in the TLS SEGMENT to equal this TlsExpansionSlot,but without this call to TlsAlloc the area generates a access violation, which area of the ldr or event triggers causes this effect? it certainly is not in TlsAlloc and associates.. ;D

regards BanMe

Indy
December 31st, 2010, 15:10
It seems to me that you do not understand what you say. Probably the reason for this - the new year

BanMe
December 31st, 2010, 15:33
oh i see what u mean, my error was I assumed the index was 0.. o0

Code such as this does work..

Code:

OPTION DOTNAME
.TLS SEGMENT DWORD FLAT PUBLIC 'TLS'
_tls_start LABEL DWORD
_tls_data DWORD 07FFDEE10h
_tls_end LABEL DWORD
.TLS ENDS
OPTION NODOTNAME


Code:

TlsWrite:
push ecx
mov ecx,TLS_ENGINE.TlsData[edx];_tls_data address to pointer
mov ecx,[ecx];read address to get Pointer
mov ebx,TLS_ENGINE.TlsIndex[edx] ; get 0 Index from self
mov ebx, dword ptr [ebx]
inc ebx ;inc index to write to ExpansionSlot..
mov dword ptr [ecx+ebx*4], eax
mov ecx,TLS_ENGINE.TlsIndex[edx]
mov dword ptr [ecx],ebx
pop ecx
mov eax,ecx
jmp TlsReturn
TlsRead:
push ecx
mov ecx,TLS_ENGINE.TlsData[edx]
mov ecx,[ecx]
mov eax,TLS_ENGINE.ReadIndex[edx]
mov ebx,TLS_ENGINE.TlsIndex[edx]
mov ebx,dword ptr [ebx]
cmp eax,ebx
jge TlsReturn
mov eax,dword ptr [ecx+eax*4]
pop ecx


but then my question is why is the TlsBitmap modified. what uses this...

wait alex!!! my real answer is, what is 'LdrpTlsCount'!! and the address please.. for 200$...xD

It is new years and I was searching this functionality in reactos, but wanted to find it in my own ntdll, i started by looking for code sequences, that i thought might be in this. that did work, then I got the idea instead of looking for that look for something that 'has' to be there.. ie..
Code:

7C9227AE . 50 PUSH EAX
7C9227AF . 6A 09 PUSH 9
7C9227B1 . 6A 01 PUSH 1
7C9227B3 . FF77 18 PUSH DWORD PTR DS:[EDI+18]
7C9227B6 . E8 8BDBFEFF CALL ntdll.RtlImageDirectoryEntryToData

So I searched for this call and had many hits some interesting ones.. but tls was my main goal so I looked for IMAGE_DIRECTORY_ENTRY_TLS or the 9 u see above..

there are 2.. so this cut it down o0.. so there 2 occurances in ntdll access trying to 'access' the .tls section of a module.

But the one I am interested in is the one That is called LdrpInitializeTlsForProcess...to make sure of what happens when my exe with emulated tls gets handled correctly and what can be done if it does not get handled correctly..

*side note to niaren* What happens if the image has tls functions and these are not included in the reloc section.. I saw a function that relocs Tls as well somewhere but just noted it for interest..

Indy
January 6th, 2011, 15:56
BanMe

Optimal search using the message logger. Logger prints messages if ntdll!ShowSnaps. In this case, you need two messages:
LdrpInitializeTls: ASCII "LDR: Tls Found in %wZ at %p",LF
LdrpAllocateTls: ASCII "LDR: TlsVector %x Index %d = %x copied from %x to %x",LF

LdrpCallTlsInitializers: ASCII "LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p",LF

Possible find the code references to these lines, ie a reference to the body of the required procedure. To determine the beginning of the procedure is acceptable to use the analysis of the graph. When using the GPE pseudocode as follows:
Code:
PARSE_CALLBACK_ROUTINE(GE:PGRAPH_ENTRY, CallList:PCALL_ENTRY):
if GE.Type = TYPE_CALL
if GE.BranchAddress = @DbgPrint()
GE = GE.Blink
if GE.Type = TYPE_LINE
if BYTE[GE.Address] = 0x68
if StrCmp(TargetMessage, DWORD[GE.Address + 1])
Routine = CallList[0]

Code for it:
Code:
.data
NtBase PVOID ?
NtLimit PVOID ?
gRef1 PCALL_HEADER ?
gRef2 PCALL_HEADER ?

.code
$Fn1 CHAR "Tls Found in %wZ",0 ; LdrpInitializeTls()
$Fn2 CHAR "TlsVector %x",0 ; LdrpAllocateTls()

; o GCBE_PARSE_OPENLIST
;
PARSE_CALLBACK_ROUTINE proc uses ebx Graph:PVOID,
GraphEntry:PVOID,
SubsList:PVOID,
SubsCount:ULONG,
PreOrPost:BOOLEAN,
Context:PVOID
mov ebx,GraphEntry
mov eax,dword ptr [ebx + EhEntryType]
and eax,TYPE_MASK
cmp eax,HEADER_TYPE_CALL
jne @f
; (!BRANCH_DEFINED_FLAG)
mov ecx,dword ptr [ebx + EhAddress]
cmp byte ptr [ecx],0E8H
jne @f
mov eax,dword ptr [ebx + EhBranchAddress]
cmp dword ptr [_imp__DbgPrint],eax
jne @f
cmp byte ptr [ecx - 5],68H
jne @f
mov ebx,dword ptr [ecx - 4]
cmp NtBase,ebx
jnb @f
cmp NtLimit,ebx
jbe @f
cmp gRef1,NULL
jne Check2
invoke InString, 1, Ebx, addr $Fn1
test eax,eax
jle Check2
mov eax,SubsList
mov eax,dword ptr [eax]
mov gRef1,eax
@@:
xor eax,eax
Exit:
ret
xBreak:
mov eax,STATUS_WAIT_1
jmp Exit
Check2:
cmp gRef2,NULL
jne xBreak
invoke InString, 1, Ebx, addr $Fn2
test eax,eax
jle @b
mov eax,SubsList
mov eax,dword ptr [eax]
mov gRef2,eax
jmp @b
PARSE_CALLBACK_ROUTINE endp

Graph is created for LdrInitializeThunk().

With dynamic initialization TLS directory is added to the module, as I can remember at this address range is not checked, so the directory can be outside of the module. You can hide a directory of destroying the module and processed #AV, but this is unnecessary.

BanMe
January 13th, 2011, 22:27
lol I missed this, somehow, ty for the examples :].