Map and projects (the most frequently updated page of this blog)

2010/02/09

Militant quotidien de l'inhumanité

TLS and Imports

When is an apparently incorrect TLS entry actually executing a file ?

I already introduced TLS:
before execution of the Entrypoint, each callback is taken as is - since it's a VA - and executed, until a null entry or an exception occurs.

linked to Imports

But if you make the callbacks point to one of the imports:
AddressOfCallBacks dd __imp__WinExec

In the file, the import to WinExec will originally point to a Hint+Name structure:
WinExec:
jmp [__imp__WinExec]

__imp__WinExec:
DD aWinExec - IMAGEBASE ; RVA
DD 0

aWinExec:
dw 0 ; Hint
db 'WinExec',0 ; Name
so it doesn't look like it's a valid VA. At least, not yet.

Once the file is loaded, it will points to the API.
0040026C:
7C86250D kernel32.WinExec
So it's 'now' a valid VA, and TLS execution will happen correctly.

Arguments

You're calling an API, but you just don't control the parameters.
If it's your own DLL, you could still do anything. In any case, the first parameter on the stack is actually the IMAGEBASE, which points to the start of the file. Thus, MZ... and nothing prevents this signature to be taken as a normal parameter:
EIP 7C86250D kernel32.WinExec
Stack:
7C90118A CALL to WinExec from ntdll.7C901187
00400000 CmdLine = "MZ"
00000001 ShowState = SW_SHOWNORMAL
(7C90118A corresponds to NtDLL's LdrInitializeThunk + 24, which is indeed TLS execution return)

So, what will it do ? Execute a file named MZ. Or, if you modify the header a bit more, a file named MZsomething.

Thus, with an obscure TLS entry, you get a free pre-entry point API call - here, a file execution.

This is a funny trick by Peter Ferrie in his comment.

Binary and source




TLS et Imports

Quand est-ce qu'un TLS apparemment incorrect exécute correctement un fichier ?

J'ai déjà parlé du TLS:
avant l'exécution de l'Entrypoint, chaque callback est pris tel quel - puisque c'est une adresse virtuelle - et exécuté, jusqu'à une entrée nulle ou qu'une exception se produise.

TLS et imports

Mais si on fait pointer un des callbacks vers un des imports:
AddressOfCallBacks dd __imp__WinExec

Dans le fichier, l'import vers WinExec pointe initialement vers une structure Hint+Name:
WinExec:
jmp [__imp__WinExec]

__imp__WinExec:
DD aWinExec - IMAGEBASE ; RVA
DD 0

aWinExec:
dw 0 ; Hint
db 'WinExec',0 ; Name
donc ça n'a pas l'air d'une VA valide. Du moins, pas pour l'instant.

Une fois que le fichier sera chargé, cela pointera vers l'API en mémoire.
0040026C:
7C86250D kernel32.WinExec
Donc c'est 'à présent' une VA valide, et l'exécution du TLS se produira correctement.

Paramètres

Vous appelez donc une API, sans contrôler les paramètres.
Si c'est votre propre DLL, vous pouvez faire ce que vous voulez. Dans tous les cas, le premier paramètre sur la pile est l'IMAGEBASE, qui pointe vers le début du fichier. MZ, donc...et rien n'interdit cette signature d'être interprétée comme un paramètre normal:
EIP 7C86250D kernel32.WinExec
Stack:
7C90118A CALL to WinExec from ntdll.7C901187
00400000 CmdLine = "MZ"
00000001 ShowState = SW_SHOWNORMAL
(7C90118A correspond à NtDLL LdrInitializeThunk + 24, qui est effectivement le point de retour des TLS)

Donc, que va-t-il se passer ? Un fichier nommé MZ sera exécuté. Ou, si on modifie l'en-tête, un fichier nommé MZquelquechose.

Donc, avec un TLS bizarre, on obtient gratuitement un appel d'API avant l'EntryPoint - dans le cas présent, l'exécution d'un fichier.

Merci à Peter Ferrie pour cette astuce dans son commentaire.

Binaire et source

No comments:

Post a Comment