65.9K
CodeProject 正在变化。 阅读更多。
Home

在 WebBrowser 中检索 HttpOnly 会话 Cookie

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (12投票s)

2009年7月31日

CPOL

2分钟阅读

viewsIcon

688448

downloadIcon

8053

HttpOnly 是 IE 6 SP1、Opera、Safari 和 KDE 中的一项功能,允许设置仅通过 HTTP 标头发送且无法通过客户端脚本访问的 Cookie。

引言

为了帮助减轻跨站点脚本攻击的风险,微软 Internet Explorer 6 SP1 引入了一项新功能。此功能是 Cookie 的一个新属性,它阻止通过客户端脚本访问 Cookie。具有此属性的 Cookie 称为 HttpOnly Cookie。  

演示程序在启动时访问网页 https://codeproject.org.cn/index.aspx,并获取响应 HTTP 标头: 

HTTP/1.1 200 OK
Cache-Control: private
Date: Fri, 31 Jul 2009 08:38:03 GMT
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Set-Cookie: SessionGUID=03e40347-534c-4160-8936-19745e6dae20; path=/
Set-Cookie: ASP.NET_SessionId=beegcn2fu2pmjjeyvdu1uzrv; path=/; HttpOnly
Transfer-Encoding: chunked  

Cookie ASP.Net_SessionId 被标记为 HttpOnly,无法通过 IHTMLDocument2::get_cookie 方法获取。 

背景 

WebBrowser (mshtml.dll) 通过调用 WININET.dll 暴露的方法访问 HTTP Web 服务器。

挂钩 WININET.DLL 暴露的方法可以与发送到服务器的每个请求进行交互,包括 AJAX 请求! 

可能的调用序列如下所示: 

1=> InternetOpen
2=> InternetConnect
3=> HttpOpenRequest
// .............
X=> InternetCloseHandle  
通过挂钩这些方法,我们可以检测发送到 Web 服务器的每个请求,并在每个请求之前/之后注入我们的代码。

为什么不使用 WINSOCK?由于前者封装了 HTTP 协议,因此挂钩 WININET.DLL 暴露的方法比挂钩 WINSOCK 方法更容易。

挂钩有两种不同的方式,即内联挂钩和例程挂钩。内联挂钩几乎适用于所有情况,但是例程挂钩更健壮。该示例通过修改 IAT(导入地址表)来实现它,IAT 是例程挂钩的一种。

如果您需要有关 IAT 挂钩的更多详细信息,请参阅本文作为参考。 

代码片段

// required header files and library files
#include <WinInet.h>  
#pragma comment( lib, "WinInet.lib")  
#include <Dbghelp.h>  
#include <DelayImp.h>  
#pragma comment( lib, "Dbghelp.lib")  
#include <Psapi.h>  
#pragma comment( lib, "Psapi.lib")  
// The following macro can be found in IE8 SDK
#ifndef INTERNET_COOKIE_NON_SCRIPT
#define INTERNET_COOKIE_NON_SCRIPT      0x00001000
#endif

#ifndef INTERNET_COOKIE_HTTPONLY
#define INTERNET_COOKIE_HTTPONLY      0x00002000
#endif
// The following macro can be found in DDK/WDK header files
typedef struct _STRING {  
    USHORT  Length;  
    USHORT  MaximumLength;  
    PCHAR   Buffer;  
} ANSI_STRING, *PANSI_STRING;  
typedef struct _LSA_UNICODE_STRING {  
    USHORT Length;  
    USHORT MaximumLength;  
    PWSTR  Buffer;  
}LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;  
typedef LONG NTSTATUS;  

#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) 
#endif
// The function pointer statements for those function to hook
typedef NTSTATUS (WINAPI* PFN_LdrGetProcedureAddress)
	(IN HMODULE ModuleHandle, IN PANSI_STRING FunctionName OPTIONAL, 
	IN WORD Oridinal OPTIONAL, OUT PVOID *FunctionAddress );  
typedef NTSTATUS (WINAPI* PFN_LdrLoadDll)(IN PWCHAR PathToFile OPTIONAL, 
	IN ULONG Flags OPTIONAL, IN PUNICODE_STRING ModuleFileName, 
	OUT PHANDLE ModuleHandle);  
typedef BOOL (WINAPI* PFN_HttpSendRequestA)(HINTERNET hRequest, 
	LPCSTR lpszHeaders, DWORD dwHeadersLength, 
	LPVOID lpOptional, DWORD dwOptionalLength );  
typedef BOOL (WINAPI* PFN_HttpSendRequestW)
	(HINTERNET hRequest, LPCWSTR lpszHeaders, 
	DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength );  
typedef BOOL (WINAPI* PFN_HttpSendRequestExA)
	( __in HINTERNET hRequest, __in_opt LPINTERNET_BUFFERSA lpBuffersIn, 
	__out_opt LPINTERNET_BUFFERSA lpBuffersOut, __in DWORD dwFlags, 
	__in_opt DWORD_PTR dwContext);  
typedef BOOL (WINAPI* PFN_HttpSendRequestExW)
	( __in HINTERNET hRequest, __in_opt LPINTERNET_BUFFERSW lpBuffersIn, 
	__out_opt LPINTERNET_BUFFERSW lpBuffersOut, __in DWORD dwFlags, 
	__in_opt DWORD_PTR dwContext);  
typedef BOOL (WINAPI* PFN_HttpEndRequestA)( __in HINTERNET hRequest, 
	__out_opt LPINTERNET_BUFFERSA lpBuffersOut, __in DWORD dwFlags, 
	__in_opt DWORD_PTR dwContext);  
typedef BOOL (WINAPI* PFN_HttpEndRequestW)( __in HINTERNET hRequest, 
	__out_opt LPINTERNET_BUFFERSW lpBuffersOut, __in DWORD dwFlags, 
	__in_opt DWORD_PTR dwContext);  
typedef HINTERNET (WINAPI* PFN_HttpOpenRequestA)
	(__in HINTERNET hConnect,__in_opt LPCSTR lpszVerb, 
	__in_opt LPCSTR lpszObjectName, __in_opt LPCSTR lpszVersion, 
	__in_opt LPCSTR lpszReferrer, __in_z_opt LPCSTR FAR * lplpszAcceptTypes, 
	__in DWORD dwFlags, __in_opt DWORD_PTR dwContext);  
typedef HINTERNET (WINAPI* PFN_HttpOpenRequestW)
	(__in HINTERNET hConnect,__in_opt LPCWSTR lpszVerb,
	__in_opt LPCWSTR lpszObjectName,__in_opt LPCWSTR lpszVersion,
	__in_opt LPCWSTR lpszReferrer,__in_z_opt LPCWSTR FAR * lplpszAcceptTypes,
	__in DWORD dwFlags, __in_opt DWORD_PTR dwContext);  
typedef HINTERNET (WINAPI* PFN_InternetConnectA)
	(__in HINTERNET hInternet,__in LPCSTR lpszServerName,
	__in INTERNET_PORT nServerPort,__in_opt LPCSTR lpszUserName,
	__in_opt LPCSTR lpszPassword,__in DWORD dwService,__in DWORD dwFlags,
	__in_opt DWORD_PTR dwContext);  
typedef HINTERNET (WINAPI* PFN_InternetConnectW)
	(__in HINTERNET hInternet,__in LPCWSTR lpszServerName,
	__in INTERNET_PORT nServerPort,__in_opt LPCWSTR lpszUserName,
	__in_opt LPCWSTR lpszPassword,__in DWORD dwService,__in DWORD dwFlags,
	__in_opt DWORD_PTR dwContext);  
typedef BOOL (WINAPI* PFN_HttpAddRequestHeadersA)
	(__in HINTERNET hRequest,__in_ecount(dwHeadersLength) LPCSTR lpszHeaders,
	__in DWORD dwHeadersLength,__in DWORD dwModifiers);  
typedef BOOL (WINAPI* PFN_HttpAddRequestHeadersW)
	(__in HINTERNET hRequest,__in_ecount(dwHeadersLength) LPCWSTR lpszHeaders,
	__in DWORD dwHeadersLength,__in DWORD dwModifiers);  
typedef HINTERNET (WINAPI* PFN_InternetOpenUrlA)
	(HINTERNET hInternet,LPCSTR lpszUrl, LPCSTR lpszHeaders, 
	DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext);
typedef HINTERNET (WINAPI* PFN_InternetOpenUrlW)
	(HINTERNET hInternet,LPCWSTR lpszUrl,LPCWSTR lpszHeaders,
	DWORD dwHeadersLength,DWORD dwFlags,DWORD_PTR dwContext);
typedef HINTERNET (WINAPI* PFN_InternetOpenA)
	( LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, 
	LPCSTR lpszProxyBypass, DWORD dwFlags );
typedef HINTERNET (WINAPI* PFN_InternetOpenW)
	( LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, 
	LPCWSTR lpszProxyBypass, DWORD dwFlags );
typedef BOOL (WINAPI* PFN_InternetCloseHandle)(__in HINTERNET hInternet);
typedef BOOL (WINAPI* PFN_InternetSetCookieA)
	(LPCSTR lpszUrl, LPCSTR lpszCookieName, LPCSTR lpszCookieData );
typedef BOOL (WINAPI* PFN_InternetSetCookieW)
	( LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData);
typedef DWORD (WINAPI* PFN_InternetSetCookieExA)
	(LPCSTR lpszUrl, LPCSTR lpszCookieName, 
	LPCSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved);
typedef DWORD (WINAPI* PFN_InternetSetCookieExW)
	( LPCWSTR lpszUrl, LPCWSTR lpszCookieName, 
	LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved );
class CWininetHook
{
public:
	CWininetHook(void);
	~CWininetHook(void);

private:
	// variables to store the original function address
	static PFN_LdrLoadDll s_pfnLdrLoadDll;  
	static PFN_LdrGetProcedureAddress s_pfnLdrGetProcedureAddress;  
	static PFN_HttpSendRequestA s_pfnHttpSendRequestA;  
	static PFN_HttpSendRequestW s_pfnHttpSendRequestW;  
	static PFN_HttpSendRequestExA s_pfnHttpSendRequestExA;  
	static PFN_HttpSendRequestExW s_pfnHttpSendRequestExW;  
	static PFN_HttpEndRequestA s_pfnHttpEndRequestA;  
	static PFN_HttpEndRequestW s_pfnHttpEndRequestW;  
	static PFN_HttpOpenRequestA s_pfnHttpOpenRequestA;  
	static PFN_HttpOpenRequestW s_pfnHttpOpenRequestW;  
	static PFN_InternetConnectA s_pfnInternetConnectA;  
	static PFN_InternetConnectW s_pfnInternetConnectW;    
	static PFN_HttpAddRequestHeadersA s_pfnHttpAddRequestHeadersA;  
	static PFN_HttpAddRequestHeadersW s_pfnHttpAddRequestHeadersW;  
	static PFN_InternetOpenUrlA s_pfnInternetOpenUrlA;  
	static PFN_InternetOpenUrlW s_pfnInternetOpenUrlW;  
	static PFN_InternetOpenA s_pfnInternetOpenA;  
	static PFN_InternetOpenW s_pfnInternetOpenW;  
	static PFN_InternetCloseHandle s_pfnInternetCloseHandle;
	static PFN_InternetSetCookieA s_pfnInternetSetCookieA;
	static PFN_InternetSetCookieW s_pfnInternetSetCookieW;
	static PFN_InternetSetCookieExA s_pfnInternetSetCookieExA;
	static PFN_InternetSetCookieExW s_pfnInternetSetCookieExW;
};

static CWininetHook g_oHook;  

// initialize the static variables  
PFN_LdrLoadDll CWininetHook::s_pfnLdrLoadDll = NULL;  
PFN_LdrGetProcedureAddress CWininetHook::s_pfnLdrGetProcedureAddress = NULL;  

// save the original address
PFN_HttpSendRequestA CWininetHook::s_pfnHttpSendRequestA = HttpSendRequestA;  
PFN_HttpSendRequestW CWininetHook::s_pfnHttpSendRequestW = HttpSendRequestW;  
PFN_HttpAddRequestHeadersA CWininetHook::s_pfnHttpAddRequestHeadersA = 
						HttpAddRequestHeadersA;  
PFN_HttpAddRequestHeadersW CWininetHook::s_pfnHttpAddRequestHeadersW = 
						HttpAddRequestHeadersW;  
PFN_HttpSendRequestExA CWininetHook::s_pfnHttpSendRequestExA = HttpSendRequestExA;  
PFN_HttpSendRequestExW CWininetHook::s_pfnHttpSendRequestExW = HttpSendRequestExW;  
PFN_HttpEndRequestA CWininetHook::s_pfnHttpEndRequestA = HttpEndRequestA;  
PFN_HttpEndRequestW CWininetHook::s_pfnHttpEndRequestW = HttpEndRequestW;  
PFN_HttpOpenRequestA CWininetHook::s_pfnHttpOpenRequestA = HttpOpenRequestA;  
PFN_HttpOpenRequestW CWininetHook::s_pfnHttpOpenRequestW = HttpOpenRequestW;  
PFN_InternetConnectA CWininetHook::s_pfnInternetConnectA = InternetConnectA;  
PFN_InternetConnectW CWininetHook::s_pfnInternetConnectW = InternetConnectW;  
PFN_InternetOpenUrlA CWininetHook::s_pfnInternetOpenUrlA = InternetOpenUrlA;  
PFN_InternetOpenUrlW CWininetHook::s_pfnInternetOpenUrlW = InternetOpenUrlW;  
PFN_InternetOpenA CWininetHook::s_pfnInternetOpenA = InternetOpenA;
PFN_InternetOpenW CWininetHook::s_pfnInternetOpenW = InternetOpenW;
PFN_InternetCloseHandle CWininetHook::s_pfnInternetCloseHandle = InternetCloseHandle;
PFN_InternetSetCookieA CWininetHook::s_pfnInternetSetCookieA = InternetSetCookieA;
PFN_InternetSetCookieW CWininetHook::s_pfnInternetSetCookieW = InternetSetCookieW;
PFN_InternetSetCookieExA CWininetHook::s_pfnInternetSetCookieExA = InternetSetCookieExA;
PFN_InternetSetCookieExW CWininetHook::s_pfnInternetSetCookieExW = InternetSetCookieExW;
 // Enumerate all the loaded modules in the current process,
 // and search for the IAT to be processed
void CWininetHook::ReplaceIATEntryForAll(LPCSTR lpszDllName, 
				LPVOID pfnCurrent, LPVOID pfnNew)  
{  
    HMODULE hMods[1024] = {0};  
    DWORD cbNeeded;  
    HANDLE hProcess = ::GetCurrentProcess();  
    if( ::EnumProcessModules( hProcess, hMods, sizeof(hMods), &cbNeeded))  
    {  
        for ( UINT i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )  
        {  
            ReplaceIATEntryInImageImportTable( hMods[i]  
            , lpszDllName  
                , pfnCurrent  
                , pfnNew  
                );  
        }  
    }  
}  
// search in IMAGE_IMPORT_DESCRIPTOR for the special module
BOOL CWininetHook::ReplaceIATEntryInImageImportTable( HANDLE hBaseAddress  
                                                     , LPCSTR lpszDllName  
                                                     , LPVOID pfnCurrent  
                                                     , LPVOID pfnNew  
                                                     )  
{  
    ASSERT(hBaseAddress && lpszDllName && pfnCurrent && pfnNew );  
    // retrieve IMAGE_IMPORT_DESCRIPTOR  
    DWORD dwSize = 0;  
    PIMAGE_SECTION_HEADER pFoundHeader = NULL;  
    PIMAGE_IMPORT_DESCRIPTOR pImgImportDescriptor  
        = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToDataEx( hBaseAddress  
        , TRUE  
        , IMAGE_DIRECTORY_ENTRY_IMPORT  
        , &dwSize  
        , &pFoundHeader  
        );  
    if( pImgImportDescriptor == NULL ){ return FALSE; }  
    while (pImgImportDescriptor->Name)  
    {  
        if ( _strcmpi((CHAR*)((PBYTE)hBaseAddress+pImgImportDescriptor->Name), 
							lpszDllName) == 0 )  
        {  
            break; // found  
        }  
        ++pImgImportDescriptor;  
    }  
    // NOTE:
    // If the special module can not be found in IMAGE_DIRECTORY_ENTRY_IMPORT
    // Then should try to search it in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
    if( !pImgImportDescriptor->Name )  
        return ReplaceIATEntryInDelayImageImportTable
		( hBaseAddress, lpszDllName, pfnCurrent, pfnNew);  

    // retrieve IAT  
    PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
	(((LPBYTE)hBaseAddress) + pImgImportDescriptor->FirstThunk);  

    // enumerate functions in the IAT
    while(pThunk->u1.Function)    
    {    
        PDWORD lpAddr = (PDWORD)&(pThunk->u1.Function);    
        if(*lpAddr == (DWORD)pfnCurrent)    
        {  
            // modify the address
            ::WriteProcessMemory(::GetCurrentProcess()  
                , lpAddr  
                , &pfnNew  
                , sizeof(DWORD)  
                , NULL  
                );  
            return TRUE;  
        }     
        pThunk++;    
    }  
    return FALSE;  
}   

对于延迟加载的 DLL,请在 IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 中搜索。

// search in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT for the special module
BOOL CWininetHook::ReplaceIATEntryInDelayImageImportTable( HANDLE hBaseAddress  
                                                          , LPCSTR lpszDllName  
                                                          , LPVOID pfnCurrent  
                                                          , LPVOID pfnNew  
                                                          )  
{  
    ASSERT(hBaseAddress && lpszDllName && pfnCurrent && pfnNew );  
    // retrieve IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT  
    DWORD dwSize = 0;  
    PIMAGE_SECTION_HEADER pFoundHeader = NULL;  
    PImgDelayDescr pImgDelayDescr  
        = (PImgDelayDescr)ImageDirectoryEntryToDataEx( hBaseAddress  
        , TRUE  
        , IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT  
        , &dwSize  
        , &pFoundHeader  
        );  
    if( pImgDelayDescr == NULL ){ return FALSE; }  
    while (pImgDelayDescr->rvaDLLName)  
    {  
        if ( _strcmpi((CHAR*)((PBYTE)hBaseAddress+pImgDelayDescr->rvaDLLName), 
							lpszDllName) == 0 )  
        {  
            break;  
        }  
        ++pImgDelayDescr;  
    }  
    // Not found in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
    if( !pImgDelayDescr->rvaDLLName )  
        return FALSE;  

    // retrieve IAT  
    PIMAGE_THUNK_DATA pThunk = NULL;  
    if( (pImgDelayDescr->grAttrs & dlattrRva) == 0 )  
        return FALSE;  
    pThunk = (PIMAGE_THUNK_DATA)(((LPBYTE)hBaseAddress) + pImgDelayDescr->rvaIAT); 

    // enumerate functions in the IAT
    while(pThunk->u1.Function)    
    {    
        PDWORD lpAddr = (PDWORD)&(pThunk->u1.Function);    
        if(*lpAddr == (DWORD)pfnCurrent)    
        {  
            // modify the address
            ::WriteProcessMemory(::GetCurrentProcess()  
                , lpAddr  
                , &pfnNew  
                , sizeof(DWORD)  
                , NULL  
                );  
            return TRUE;
        }     
        pThunk++;    
    }

    return FALSE;  
}  

Windows PE 加载器可能在进程启动后将额外的 DLL 加载到进程中,并且函数地址也可以通过 GetProcAddress API 获取,因此有必要修改新加载的 DLL 中的 IAT。

通常,会挂钩 LoadLibary(Ex)GetProcAddress 。但是,该示例挂钩 LdrLoadDll LdrGetProcedureAddress LoadLibary(Ex)GetProcAddress 的实现依赖于 ntdll.dll 中暴露的 LdrLoadDll LdrGetProcedureAddressntdll.dll 是从 Ring3 到 Ring0 的网关。

// Process those modules loaded dynamically, 
NTSTATUS WINAPI CWininetHook::_LdrLoadDll
	(IN PWCHAR PathToFile OPTIONAL, IN ULONG Flags OPTIONAL, 
	IN PUNICODE_STRING ModuleFileName, OUT PHANDLE ModuleHandle)  
{     
    NTSTATUS ntStatus = s_pfnLdrLoadDll
	( PathToFile, Flags, ModuleFileName, ModuleHandle);  
    if( ntStatus == STATUS_SUCCESS && (Flags & LOAD_LIBRARY_AS_DATAFILE) == 0 )  
    {
#ifdef _DEBUG
        if( ModuleFileName->Length > 0 )
        {
            WCHAR wszPath[MAX_PATH] = {0};  
            memcpy_s( wszPath, MAX_PATH*sizeof(WCHAR), 
		ModuleFileName->Buffer, MAX_PATH*sizeof(WCHAR));
            TRACE( L"LdrLoadDll %s\n", wszPath);
        }        
#endif                

        HANDLE hDll = *ModuleHandle;  
        ReplaceIATEntryInImageImportTable( hDll, "NTDLL.DLL", 
			s_pfnLdrLoadDll, &CWininetHook::_LdrLoadDll);  
        //......
    }  
    return ntStatus;  
}  

// Return the user-provided function address instead of the read one
NTSTATUS WINAPI CWininetHook::_LdrGetProcedureAddress
	(IN HMODULE ModuleHandle, IN PANSI_STRING FunctionName OPTIONAL, 
	IN WORD Oridinal OPTIONAL, OUT PVOID *FunctionAddress )  
{  
    NTSTATUS ntStatus = s_pfnLdrGetProcedureAddress
	( ModuleHandle, FunctionName, Oridinal, FunctionAddress);  
    if( ntStatus == STATUS_SUCCESS )  
    {  
        TCHAR tszPath[MAX_PATH] = {0};  
        if( GetModuleFileName( ModuleHandle, tszPath, MAX_PATH) )  
        {  
            CString strFile(tszPath);  
            int nFind = strFile.ReverseFind('\\');  
            if( nFind > 0 )  
                strFile = strFile.Mid(nFind+1);  
            if( strFile.CompareNoCase(_T("WININET.dll")) == 0 )  
            {  
                CHAR szFunName[1024] = {0};  
                memcpy( szFunName, FunctionName->Buffer, FunctionName->Length );  
#ifdef _DEBUG
                USES_CONVERSION;
                TRACE( _T("LdrGetProcedureAddress %s\n"), A2T(szFunName));
#endif                

                TRY_GET_ADDRESS( "HttpSendRequestA", 
		s_pfnHttpSendRequestA, _HttpSendRequestA,  PFN_HttpSendRequestA);
                // ......
             }  
        }  
    }  

    return ntStatus;  
}   

现在,我们可以注入到每个请求中,包括 AJAX 请求。如何在发送请求时获取/设置 HttpOnly Cookie?IE8 SDK 为 InternetGetCookieEx / InternetSetCookieEx 添加了一个新的标志 INTERNET_COOKIE_HTTPONLY

TCHAR tszTemp[10240] = {0};
DWORD dwSize = sizeof(tszTemp);

InternetGetCookieEx( _T("https://codeproject.org.cn/index.aspx")
                    , _T("ASP.NET_SessionId")
                    , tszTemp
                    , &dwSize
                    , INTERNET_COOKIE_HTTPONLY
                    , NULL
                    );  

InternetSetCookieEx( strUrl
                    , _T("ASP.NET_SessionId")
                    , _T("XXXXXXXXX")
                    , INTERNET_COOKIE_HTTPONLY
                    , NULL			
                    ); 

虽然 MSDN 声明“需要 Internet Explorer 8.0 或更高版本”,但它在我的 Internet Explorer 7.0 中仍然有效。

这应该只是与 WININET.dll 有关,与 mshtml.dll 无关。

© . All rights reserved.