////////////////////////////////////////////////////////////////
// jscriptpatch.cpp
//==============================================================
// Patch for the IE createTextRange() vulnerability.
//
// Derek Soeder - eEye Digital Security - 03/24/2006
////////////////////////////////////////////////////////////////
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
////////////////////////////////
// TranslateRVAToRawPtr
////////////////////////////////
PBYTE TranslateRVAToRawPtr(
PBYTEpbBase,
DWORDdwRVA)
{
PIMAGE_NT_HEADERSppe;
PIMAGE_SECTION_HEADERpsect;
DWORDdw;
//----------------
ppe = (PIMAGE_NT_HEADERS)(pbBase + ((PIMAGE_DOS_HEADER)pbBase)->e_lfanew);
psect = IMAGE_FIRST_SECTION(ppe);
for (dw = ppe->FileHeader.NumberOfSections; dw != 0; dw--, psect++)
{
if (dwRVA >= psect->VirtualAddress && (dwRVA - psect->VirtualAddress) < psect->SizeOfRawData)
return pbBase + (dwRVA - psect->VirtualAddress + psect->PointerToRawData);
} //for(dw)
return NULL;
} //TranslateRVAToRawPtr()
////////////////////////////////
// CreateJScriptPatchDLL
////////////////////////////////
BOOL CreateJScriptPatchDLL(
char*szOriginalDLL,
char*szPatchDLL)
{
HMODULEhmod;
DWORDcbmodsize;
PIMAGE_NT_HEADERSppe;
PIMAGE_SECTION_HEADERpsect;
DWORDdwsectnum;
PDWORDpdw;
PVOIDpvend;
DWORDdw;
DWORDdwrva, dwrvadest;
PBYTEpb;
LONGlofs;
DWORDdwlen;
BOOLbebp;
HANDLEhfile;
PBYTEpbdest;
DWORDcb;
DWORDdwerr;
//---------------- load the original DLL with virtual alignments and relocations applied
hmod = LoadLibraryEx(szOriginalDLL, NULL, DONT_RESOLVE_DLL_REFERENCES);
if (hmod == NULL)
return FALSE;
dwrva = 0;
dwrvadest = 0;
//---------------- search for an executable section with sufficient unused space
ppe = (PIMAGE_NT_HEADERS)(((PBYTE)hmod) + ((PIMAGE_DOS_HEADER)hmod)->e_lfanew);
if (ppe->Signature != IMAGE_NT_SIGNATURE)
{
FreeLibrary(hmod);
SetLastError(ERROR_INVALID_EXE_SIGNATURE);
return FALSE;
}
psect = IMAGE_FIRST_SECTION(ppe);
for (dwsectnum = 0; dwsectnum != ppe->FileHeader.NumberOfSections; dwsectnum++, psect++)
{
if ( (psect->Characteristics & (IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_DISCARDABLE|IMAGE_SCN_CNT_CODE)) !=
(IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_CNT_CODE) )
{
continue;
}
if ( ((psect->Misc.VirtualSize & 0x0FFF) != 0) &&
((psect->Misc.VirtualSize & 0x0FFF) <= 0x1000 - 0x20) && // patch code will be <= 0x20 bytes
(psect->SizeOfRawData > psect->Misc.VirtualSize) &&
(psect->SizeOfRawData - psect->Misc.VirtualSize >= 0x20) )
{
dwrvadest = psect->VirtualAddress + psect->Misc.VirtualSize;
break;
}
} //for(dwsectnum)
if (dwsectnum == ppe->FileHeader.NumberOfSections)
{
FreeLibrary(hmod);
SetLastError(-1);
return FALSE;
}
//---------------- search image for 0x74-case switch table
cbmodsize = ppe->OptionalHeader.SizeOfImage;
pdw = (PDWORD)hmod;
pvend = pdw + (cbmodsize / 4) - /*0x74 /*IE 5.0 SP4 only has 0x72 cases*/0x72;
for ( ; pdw != pvend; pdw = (PDWORD)((PBYTE)pdw + 1))
{
if (*pdw >= (DWORD)hmod && (*pdw - (DWORD)hmod) < cbmodsize)
{
for (dw = 1; dw != /*0x74*/0x72; dw++)
{
if (pdw[dw] < (DWORD)hmod || (pdw[dw] - (DWORD)hmod) > cbmodsize)
break;
}
if ( dw == /*0x74*/0x72 &&// overlapping cases are a "signature" for the switch table
pdw[0x00] == pdw[0x02] &&
pdw[0x00] == pdw[0x63] &&
pdw[0x00] == pdw[0x64] &&
pdw[0x01] == pdw[0x5A] &&
pdw[0x17] == pdw[0x1B] &&
pdw[0x5E] == pdw[0x5F] )
{
break;
}
}
} //for(pdw)
if (pdw == pvend)
{
FreeLibrary(hmod);
SetLastError(-1);
return FALSE;
}
//---------------- search case 0x23 code for VT_EMPTY assignment instruction
pb = (PBYTE)(pdw[0x23]);
lofs = 0;
dwlen = 0;
bebp = FALSE;
for (dw = 0x40; dw != 0; dw--, pb++)
{
if (pb[0x00] != 0x66)// 66h: operand size prefix ('vt' is a WORD-size field)
continue;
if (pb[0x01] == 0xC7)// C7h/0: MOV mem, imm
{
switch (pb[0x02])
{
case 0x44:// ModRM=44h: 8-bit offset, SIB
if (pb[0x03] != 0x24 || pb[0x05] != 0 || pb[0x06] != 0) // SIB=24h: ESP
continue;
lofs = (LONG)*(char*)(pb + 0x04);
dwlen = 0x07;
bebp = FALSE;
break;
case 0x45:// ModRM=45h: 8-bit offset, EBP
if (pb[0x04] != 0 || pb[0x05] != 0)
continue;
lofs = (LONG)*(char*)(pb + 0x03);
dwlen = 0x06;
bebp = TRUE;
break;
case 0x84:// ModRM=84h: 32-bit offset, SIB
if (pb[0x03] != 0x24 || pb[0x08] != 0 || pb[0x09] != 0) // SIB=24h: ESP
continue;
lofs = *(LONG*)(pb + 0x04);
dwlen = 0x0A;
bebp = FALSE;
break;
case 0x85:// ModRM=85h: 32-bit offset, EBP
if (pb[0x07] != 0 || pb[0x08] != 0)
continue;
lofs = *(LONG*)(pb + 0x03);
dwlen = 0x09;
bebp = TRUE;
break;
default:
continue;
}
}
else if (pb[0x01] == 0x83)// 83h/4: AND mem, simm8
{
switch (pb[0x02])
{
case 0x64:// ModRM=64h: 8-bit offset, SIB
if (pb[0x03] != 0x24 || pb[0x05] != 0) //SIB=24h: ESP
continue;
lofs = (LONG)*(char*)(pb + 0x04);
dwlen = 0x06;
bebp = FALSE;
break;
case 0x65:// ModRM=65h: 8-bit offset, EBP
if (pb[0x04] != 0)
continue;
lofs = (LONG)*(char*)(pb + 0x03);
dwlen = 0x05;
bebp = TRUE;
break;
case 0xA4:// ModRM=A4h: 32-bit offset, SIB
if (pb[0x03] != 0x24 || pb[0x08] != 0) // SIB=24h: ESP
continue;
lofs = *(LONG*)(pb + 0x04);
dwlen = 0x09;
bebp = FALSE;
break;
case 0xA5:// ModRM=A5h: 32-bit offset, EBP
if (pb[0x07] != 0)
continue;
lofs = *(LONG*)(pb + 0x03);
dwlen = 0x08;
bebp = TRUE;
break;
default:
continue;
}
}
elsecontinue;
if ((lofs & 3) != 0)// variable will always be DWORD-aligned on stack
continue;
if (bebp)
{
if (lofs > -0x04 || lofs < -0x800)// EBP-relative must be negative
continue;
}
else
{
if (lofs < 0 || lofs > 0x800)// ESP-relative must be positive
continue;
}
dwrva = (UINT_PTR)pb - (UINT_PTR)hmod;
break;
} //for(dw)
FreeLibrary(hmod);
if (dw == 0)
{
SetLastError(-1);
return FALSE;
}
//---------------- read original JScript.dll into memory
hfile = CreateFile(szOriginalDLL, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
if (hfile == INVALID_HANDLE_VALUE)
return FALSE;
cbmodsize = GetFileSize(hfile, NULL);
if (cbmodsize == INVALID_FILE_SIZE)
{
dwerr = GetLastError();
CloseHandle(hfile);
SetLastError(dwerr);
return FALSE;
}
pb = (PBYTE)LocalAlloc(LMEM_FIXED, cbmodsize);
if (pb == NULL)
{
dwerr = GetLastError();
CloseHandle(hfile);
SetLastError(dwerr);
return FALSE;
}
if (!ReadFile(hfile, pb, cbmodsize, &cb, NULL) || cb != cbmodsize)
{
dwerr = GetLastError();
CloseHandle(hfile);
LocalFree(pb);
SetLastError(dwerr);
return FALSE;
}
CloseHandle(hfile);
//---------------- patch in-memory file and write it to patched DLL
ppe = (PIMAGE_NT_HEADERS)(pb + ((PIMAGE_DOS_HEADER)pb)->e_lfanew);
psect = IMAGE_FIRST_SECTION(ppe);
psect += dwsectnum;
pbdest = TranslateRVAToRawPtr(pb, dwrvadest);
psect->Misc.VirtualSize += 0x20;
pbdest[0x00] = 0x60;// PUSHAD
pbdest[0x01] = 0x8D;// LEA EDI, [{ESP,EBP}+ofs32]
pbdest[0x02] = 0xBC;
pbdest[0x03] = (bebp ? 0x25 : 0x24);
*(LONG*)(pbdest + 0x04) = (bebp ? lofs : lofs + 0x20);// (0x20 to compensate for PUSHAD effect on stack)
pbdest[0x08] = 0x33;// XOR EAX, EAX
pbdest[0x09] = 0xC0;
*(DWORD*)(pbdest + 0x0A) = 0xABABABAB;// STOSD / STOSD / STOSD / STOSD
pbdest[0x0E] = 0x61;// POPAD
pbdest[0x0F] = 0xE9;// JMP rel32
*(DWORD*)(pbdest + 0x10) = (dwrva + dwlen) - (dwrvadest + 0x14);
pbdest = TranslateRVAToRawPtr(pb, dwrva);
pbdest[0x00] = 0xE9;// JMP rel32
*(DWORD*)(pbdest + 0x01) = dwrvadest - (dwrva + 5);
hfile = CreateFile(szPatchDLL, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile == INVALID_HANDLE_VALUE)
{
dwerr = GetLastError();
LocalFree(pb);
SetLastError(dwerr);
return FALSE;
}
if (!WriteFile(hfile, pb, cbmodsize, &dw, NULL) || dw != cbmodsize)
{
dwerr = GetLastError();
CloseHandle(hfile);
LocalFree(pb);
SetLastError(dwerr);
return FALSE;
}
CloseHandle(hfile);
LocalFree(pb);
SetLastError(ERROR_SUCCESS);
return TRUE;
} //CreateJScriptPatchDLL()
////////////////////////////////
// WinMain
////////////////////////////////
int WINAPI WinMain(
IN HINSTANCEhInstance,
IN HINSTANCEhPrevInstance,
IN LPSTRlpCmdLine,
IN intnShowCmd)
{
charszsystem32[MAX_PATH+8];
charszoriginal[MAX_PATH+40];
charszpatch[MAX_PATH+40];
UINTu;
DWORDdwerr;
//----------------
if ( lpCmdLine != NULL && ((strstr(lpCmdLine, "/?") != NULL) || (strstr(lpCmdLine, "-?") != NULL)) )
{
MessageBox(NULL, ":::\n"
"::: Microsoft Internet Explorer createTextRange patch\n"
"::: eEye Digital Security - www.eeye.com - 03/24/2006\n"
":::\n",
"eEye Digital Security", MB_OK);
return -1;
}
//---------------- get %SystemRoot%\system32 path
u = GetSystemDirectory(szsystem32, sizeof(szsystem32) - 1);
if (u == 0 || u >= sizeof(szsystem32) - 1)
{
dwerr = GetLastError();
return (dwerr == 0 ? ERROR_PATH_NOT_FOUND : (int)dwerr);
}
if (szsystem32[u-1] != '\\')
{
szsystem32[u] = '\\';
szsystem32[u+1] = 0;
}
elseszsystem32[u] = 0;
_snprintf(szoriginal, sizeof(szoriginal), "%sjscript.dll", szsystem32);
szoriginal[sizeof(szoriginal)-1] = 0;
_snprintf(szpatch, sizeof(szpatch), "%sjscript-eeye-patch20.dll", szsystem32);
szpatch[sizeof(szpatch)-1] = 0;
//---------------- create patched JScript.dll
if (!CreateJScriptPatchDLL(szoriginal, szpatch))
{
dwerr = GetLastError();
return (dwerr == 0 ? -1 : (int)dwerr);
}
return 0;
} //WinMain()