Uninformed: Informative Information for the Uninformed

Vol 7» 2007.May


Cookie Generation

Microsoft chose to have the GS implementation generate an image file-specific cookie. This means that each image file (executable or DLL) will have their own unique cookie. When used in conjunction with a stack frame, a function will insert its image file-specific cookie into the stack frame. This will be covered in more detail in the next section. The actual approach taken to generate an image file's cookie lives in a compiler inserted routine called __security_init_cookie. This routine is placed prior to the call to the image file's actual entry point routine and therefore is one of the first things executed. By placing it at this point, all of the image file's code will be protected by the GS cookie.

The guts of the __security_init_cookie routine are actually the most critical part to understand. At a high-level, this routine will take an XOR'd combination of the current system time, process identifier, thread identifier, tick count, and performance counter. The end result of XOR'ing these values together is what ends up being the image file's security cookie. To understand how this actually works in more detail, consider the following disassembly from an application compiled with version 14.00.50727.42 of Microsoft's compiler. Going straight to the disassembly is the best way to concretely understand the implementation, especially if one is in search of weaknesses.

Like all functions, the __security_init_cookie function starts with a prologue. It allocates storage for some local variables and initializes some of them to zero. It also initializes some registers, specifically edi and ebx which will be used later on.

.text:00403D58     push ebp
.text:00403D59     mov ebp, esp
.text:00403D5B     sub esp, 10h
.text:00403D5E     mov eax, __security_cookie
.text:00403D63     and [ebp+SystemTimeAsFileTime.dwLowDateTime], 0
.text:00403D67     and [ebp+SystemTimeAsFileTime.dwHighDateTime], 0
.text:00403D6B     push ebx
.text:00403D6C     push edi
.text:00403D6D     mov edi, 0BB40E64Eh
.text:00403D72     cmp eax, edi
.text:00403D74     mov ebx, 0FFFF0000h

As part of the end of the code above, a comparison between the current security cookie and a constant 0xbb40e64e is made. Before __security_init_cookie is called, the global __security_cookie is initialized to 0xbb40e64e. The constant comparison is used to see if the GS cookie has already been initialized. If the current cookie is equal to the constant, or the high order two bytes of the current cookie are zero, then a new cookie is generated. Otherwise, the complement of the current cookie is calculated and cookie generation is skipped.

.text:00403D79     jz  short loc_403D88
.text:00403D7B     test eax, ebx
.text:00403D7D     jz  short loc_403D88
.text:00403D7F     not eax
.text:00403D81     mov __security_cookie_complement, eax
.text:00403D86     jmp short loc_403DE8

To generate a new cookie, the function starts by querying the current system time using GetSystemTimeAsFileTime. The system time as represented by Windows is a 64-bit integer that measures the system time down to a granularity of 100 nanoseconds. The high order 32-bit integer and the low order 32-bit integer are XOR'd together to produce the first component of the cookie. Following that, the current process identifier is queried using GetCurrentProcessId and then XOR'd as the second component of the cookie. The current thread identifier is then queried using GetCurrentThreadId and then XOR'd as the third component of the cookie. The current tick count is queried using GetTickCount and then XOR'd as the fourth component of the cookie. Finally, the current performance counter value is queried using QueryPerformanceCounter. Like system time, this value is also a 64-bit integer, and its high order 32-bit integer and low order 32-bit integer are XOR'd as the fifth component of the cookie. Once these XOR operations have completed, a comparison is made between the newly generated cookie value and the constant 0xbb40e64e. If the new cookie is not equal to the constant value, then a second check is made to make sure that the high order two bytes of the cookie are non-zero. If they are zero, then a 10 bit left shift of the cookie is performed in order to seed the high order bytes.

.text:00403D89     lea eax, [ebp+SystemTimeAsFileTime]
.text:00403D8C     push eax
.text:00403D8D     call ds:__imp__GetSystemTimeAsFileTime@4
.text:00403D93     mov esi, [ebp+SystemTimeAsFileTime.dwHighDateTime]
.text:00403D96     xor esi, [ebp+SystemTimeAsFileTime.dwLowDateTime]
.text:00403D99     call ds:__imp__GetCurrentProcessId@0
.text:00403D9F     xor esi, eax
.text:00403DA1     call ds:__imp__GetCurrentThreadId@0
.text:00403DA7     xor esi, eax
.text:00403DA9     call ds:__imp__GetTickCount@0
.text:00403DAF     xor esi, eax
.text:00403DB1     lea eax, [ebp+PerformanceCount]
.text:00403DB4     push eax
.text:00403DB5     call ds:__imp__QueryPerformanceCounter@4
.text:00403DBB     mov eax, dword ptr [ebp+PerformanceCount+4]
.text:00403DBE     xor eax, dword ptr [ebp+PerformanceCount]
.text:00403DC1     xor esi, eax
.text:00403DC3     cmp esi, edi
.text:00403DC5     jnz short loc_403DCE
...
.text:00403DCE loc_403DCE:
.text:00403DCE     test esi, ebx
.text:00403DD0     jnz short loc_403DD9
.text:00403DD2     mov eax, esi
.text:00403DD4     shl eax, 10h
.text:00403DD7     or  esi, eax

Finally, when a valid cookie is generated, it's stored in the image file's __security_cookie. The bit-wise complement of the cookie is also stored in __security_cookie_complement. The reason for the existence of the complement will be described later.

.text:00403DD9     mov __security_cookie, esi
.text:00403DDF     not esi
.text:00403DE1     mov __security_cookie_complement, esi
.text:00403DE7     pop esi
.text:00403DE8     pop edi
.text:00403DE9     pop ebx
.text:00403DEA     leave
.text:00403DEB     retn

In simpler terms, the meat of the cookie generation can basically be summarized through the following pseudo code:

Cookie  = SystemTimeHigh
Cookie ^= SystemTimeLow
Cookie ^= ProcessId
Cookie ^= ThreadId
Cookie ^= TickCount
Cookie ^= PerformanceCounterHigh
Cookie ^= PerformanceCounterLow