|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
una fork per Windows [x ilsensine, ma non solo :)]
l'altro giorno sulla mailing list del corso di Laboratorio di SO arriva un niubbo e fa: "ho fatto per esercitarmi un programma che usa la funzione fork bla bla e su Windows non compila (lol
) esiste una funzione analoga alla fork su Windows?"; dopodiché arriva un altro niubbo (un po' meno niubbo del precedente però) e fa "ah bla bla bla la fork è una prerogativa dei sistemi Unix ecc. ecc.".allora io prendo spunto e realizzo una mini-fork per Windows ![]() non vi immaginate che sia un granché, è molto rudimentale: altro non fa che creare il processo figlio sospeso, ricopiarci tutte le sezioni non shared e non discardable impostate con permessi di scrittura oltre che di lettura, settare opportunamente il contesto del thread interrotto, ed infine riesumarlo; una fork simile funziona solo a patto che siano verificate le seguenti condizioni: 1) il programma è single-threaded 2) il programma non chiama VirtualAlloc e HeapAlloc prima della fork (dopo volendo si, ma prima no) 3) il programma non alloca handles non ereditabili prima della fork (dopo magari si, ma prima no comunque quel poco che fa lo fa bene siccome ricordo che ilsensine una volta chiese a fek di spiegargli come hanno fatto quelli di Cygwin a realizzare la fork su Windows, ora la incollo qui di seguito; BTW, per l'occasione mi sono anche letto il codice della fork di Cygwin: la loro funziona un po' meglio della mia perché hanno un vantaggio: se ho ben capito come funziona questo Cygwin, si assume che un programma usi solamente le funzioni di Cygwin, quindi loro nel realizzare la fork avevano controllo sull'heap; ma per fare un bel crash anche su Cygwin è sufficiente tenere memoria di un handle non ereditabile prima di una fork Codice:
#include <windows.h>
int fork();
int main() {
TCHAR pszMsg[0x100];
wsprintf(pszMsg, TEXT("questo è il processo padre, PID = %d"), GetCurrentProcessId());
MessageBox(NULL, pszMsg, TEXT("fork on Windows"), MB_ICONINFORMATION);
if (fork()) {
wsprintf(pszMsg, TEXT("questo invece è il figlio, PID = %d"), GetCurrentProcessId());
MessageBox(NULL, pszMsg, TEXT("fork on Windows"), MB_ICONINFORMATION);
}
return 0;
}
#define TESTFLAG(mask, flag) ((flag) == ((mask) & (flag)))
HANDLE hStarted = NULL;
__declspec(naked) void Stub() {
SetEvent(hStarted);
CloseHandle(hStarted);
SuspendThread(GetCurrentThread());
}
BYTE pbJmp[5] = {0xE9};
int fork() {
DWORD dwDummy;
TCHAR pszFileName[0x400];
if (!GetModuleFileName(NULL, pszFileName, 0x400)) {
return -1;
}
SECURITY_ATTRIBUTES sa = {
sizeof(SECURITY_ATTRIBUTES),
NULL,
TRUE
};
hStarted = CreateEvent(&sa, FALSE, FALSE, NULL);
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory((PVOID)&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
if (!CreateProcess(pszFileName, NULL, NULL, NULL, TRUE, GetPriorityClass(GetCurrentProcess()) |
CREATE_SUSPENDED, NULL, NULL, &si, &pi))
{
return -1;
}
WriteProcessMemory(pi.hProcess, (PVOID)&hStarted, (PVOID)&hStarted, 4, &dwDummy);
PBYTE pbInstBase = (PBYTE)GetModuleHandle(NULL);
PIMAGE_NT_HEADERS pinth = (PIMAGE_NT_HEADERS)(pbInstBase + *(PDWORD)(pbInstBase + 0x3C));
PVOID pvEntryAddress = (PVOID)(pbInstBase + pinth->OptionalHeader.AddressOfEntryPoint);
DWORD dwPrevProtect;
VirtualProtectEx(pi.hProcess, pvEntryAddress, 5, PAGE_READWRITE, &dwPrevProtect);
*(PDWORD)(pbJmp + 1) = (DWORD)&Stub - (DWORD)pvEntryAddress - 5;
WriteProcessMemory(pi.hProcess, pvEntryAddress, (PVOID)pbJmp, 5, &dwDummy);
ResumeThread(pi.hThread);
WaitForSingleObject(hStarted, INFINITE);
CloseHandle(hStarted);
WriteProcessMemory(pi.hProcess, pvEntryAddress, pvEntryAddress, 5, &dwDummy);
VirtualProtectEx(pi.hProcess, pvEntryAddress, 5, dwPrevProtect, &dwDummy);
PIMAGE_SECTION_HEADER pSecHdrs = (PIMAGE_SECTION_HEADER)((PBYTE)pinth +
sizeof(IMAGE_NT_HEADERS));
for (UINT u = 0; u < pinth->FileHeader.NumberOfSections; u++) {
DWORD dwFlags = pSecHdrs[u].Characteristics;
if (!TESTFLAG(dwFlags, IMAGE_SCN_MEM_DISCARDABLE) &&
!TESTFLAG(dwFlags, IMAGE_SCN_MEM_SHARED) &&
TESTFLAG(dwFlags, IMAGE_SCN_MEM_READ) &&
TESTFLAG(dwFlags, IMAGE_SCN_MEM_WRITE))
{
PVOID pvSecAddr = (PVOID)(pbInstBase + pSecHdrs[u].VirtualAddress);
WriteProcessMemory(pi.hProcess, pvSecAddr, pvSecAddr, pSecHdrs[u].Misc.VirtualSize,
&dwDummy);
}
}
BOOL InTheChild = FALSE;
CONTEXT c;
ZeroMemory((PVOID)&c, sizeof(CONTEXT));
c.ContextFlags = CONTEXT_FULL;
GetThreadContext(GetCurrentThread(), &c);
__asm {
call near here
here:
pop eax
inc eax
mov c.Eip,eax
mov c.Esp,esp
mov c.Ebp,ebp
}
if (InTheChild) {
return GetCurrentProcessId();
}
/*
PUSH EBP
MOV EBP,ESP
*/
DWORD dwFramePtr;
__asm {
mov dwFramePtr,ebp
}
DWORD dwKernel32 = (DWORD)LoadLibrary(TEXT("kernel32.dll"));
PIMAGE_NT_HEADERS pinthKernel32 = (PIMAGE_NT_HEADERS)(dwKernel32 +
*(PDWORD)(dwKernel32 + 0x3C));
UINT uKernel32Size = pinthKernel32->OptionalHeader.SizeOfImage;
while (dwFramePtr) {
DWORD dwRetAddress = *(((PDWORD)dwFramePtr) + 1);
if ((UINT)(dwRetAddress - dwKernel32) <= uKernel32Size) {
break;
}
dwFramePtr = *(PDWORD)dwFramePtr;
}
DWORD dwStackPtr;
__asm {
mov dwStackPtr,esp
}
DWORD dwDelta = dwFramePtr - dwStackPtr;
WriteProcessMemory(pi.hProcess, (PVOID)dwStackPtr, (PVOID)dwStackPtr, dwDelta, &dwDummy);
BOOL RemoteChildFlag = TRUE;
WriteProcessMemory(pi.hProcess, (PVOID)&InTheChild, (PVOID)&RemoteChildFlag, 4, &dwDummy);
SetThreadContext(pi.hThread, &c);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
}
|
|
|
|
|
|
#2 |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
dimenticavo: a causa di una particolarità del mio codice, il programma che chiama quella fork non può neanche chiamarla da una funzione di callback che è stata a sua volta richiamata da Kernel32.dll: altre DLL vanno bene, User32 ad esempio va bene, ma Kernel32 no
insomma sta fork fa abbastanza schifo ma provate voi a farne una migliore su Windows ![]() però c'è di buono che in teoria un programma che usa quella fork può anche avere un'interfaccia grafica perché gli handles delle finestre sono globalmente visibili, e quindi sarebbero validi anche nei processi figli... con l'unico problema che il processo figlio utilizzerebbe le stesse finestre del padre |
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: May 2000
Messaggi: 1459
|
certo ke tu e kernel32.dll vi parlate eh?
ciau |
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Sep 2002
Città: Celano (AQ) Segno_Zodiacale: Leone Ascendente: Cammello Segni_Particolari: Quello
Messaggi: 9571
|
|
|
|
|
|
|
#5 |
|
Senior Member
Iscritto dal: Apr 2003
Città: Genova
Messaggi: 4739
|
bello, bello, tutta "arte che entra"
ancora inpùt, ancora...
__________________
Jappilas is a character created by a friend for his own comic - I feel honored he allowed me to bear his name Saber's true name belongs to myth - a Heroic Soul out of legends, fighting in our time to fullfill her only wish Let her image remind of her story, and of the emotions that flew from my heart when i assisted to her Fate
|
|
|
|
|
|
#6 |
|
Senior Member
Iscritto dal: May 2000
Messaggi: 1459
|
a grandi linee l'ho capita, mi manca solo una parte:
Codice:
*(PDWORD)(pbJmp + 1) = (DWORD)&Stub - (DWORD)pvEntryAddress - 5; WriteProcessMemory(pi.hProcess, pvEntryAddress, (PVOID)pbJmp, 5, &dwDummy); ciau |
|
|
|
|
|
#7 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
ciao |
|
|
|
|
|
|
#8 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
|
|
|
|
|
|
|
#9 | ||
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
Quote:
Grazie per la spiegazione comunque
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
||
|
|
|
|
|
#10 | |
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
|
|
|
|
|
|
#11 | |
|
Senior Member
Iscritto dal: Sep 2002
Città: Celano (AQ) Segno_Zodiacale: Leone Ascendente: Cammello Segni_Particolari: Quello
Messaggi: 9571
|
Quote:
|
|
|
|
|
|
|
#12 | ||||
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
il nuovo programma non avrà i thread del primo, avrà solo il thread primario, quindi o il programma è single threaded oppure il programmatore deve essere consapevole della limitazione. Quote:
Quote:
Quote:
|
||||
|
|
|
|
|
#13 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
1) il processo padre crea un handle non ereditabile per, diciamo, un file, e non lo chiude 2) fork 3) il processo figlio fa riferimento a quell'handle... al punto 3 qualsiasi funzione API semplicemente ritorna errore, ma il processo ne deve essere "aware"; cioè, se tu mi dici che il problema esiste anche su Linux e che i programmatori che usano la fork ne sono coscienti allora va bene, volevo solo dire che è un punto da tenere presente... Ultima modifica di 71104 : 14-12-2005 alle 00:00. |
|
|
|
|
|
|
#14 | ||||
|
Senior Member
Iscritto dal: Apr 2000
Città: Roma
Messaggi: 15625
|
Quote:
Quote:
Vuoi dire che questo: Quote:
Quote:
__________________
0: or %edi, %ecx; adc %eax, (%edx); popf; je 0b-22; pop %ebx; fadds 0x56(%ecx); lds 0x56(%ebx), %esp; mov %al, %al andeqs pc, r1, #147456; blpl 0xff8dd280; ldrgtb r4, [r6, #-472]; addgt r5, r8, r3, ror #12 |
||||
|
|
|
|
|
#15 | |
|
Bannato
Iscritto dal: Feb 2005
Città: Roma
Messaggi: 7029
|
Quote:
il problema è che su Windows un processo crea uno o più heap usando HeapCreate (poi li distruggerà con HeapDestroy), e in ciascuno di questi può allocare e riallocare memoria con HeapAlloc e HeapRealloc; siccome è anche possibile enumerare tutti gli heap presenti nel processo, io potrei anche provare ad enumerarli e allocare tanti blocchi nel nuovo quanti ne vedo nel vecchio, ma non è detto che avrebbero tutti gli stessi indirizzi virtuali: servirebbero delle rilocazioni... il problema si può risolvere utilizzando VirtualAlloc al posto di HeapAlloc per allocare i blocchi dell'heap: VirtualAlloc infatti permette di specificare l'indirizzo virtuale desiderato, ma c'è un problema anche qui: gli heap in Windows sono oggetti del kernel, ovvero sono identificati da un handle, il quale ovviamente viene creato come non ereditabile; se fosse ereditabile sarebbe un gran macello perché il processo figlio accederebbe alla stessa memoria dinamica del padre senza sincronizzarsi... d'altra parte però, non essendo ereditabile, il processo figlio non vede gli handle degli heap che aveva il padre, e quindi (siccome si suppone che esso tenga memoria dell'handle dell'heap, magari all'interno del CRT di Visual C++) qualsiasi HeapAlloc, HeapRealloc ed HeapFree per lui fallirà. in user mode insomma non si va molto lontano, a meno che non si faccia come hanno fatto su Cygwin, ovvero non si realizza un intero framework che abbia controllo su queste cose e quindi abbia anche controllo sugli handle degli heap e li possa cambiare a piacere. quindi stavo pensando l'altro giorno a quale potrebbe essere una soluzione un po' migliore realizzata in kernel mode: in kernel mode dopo aver creato il processo si potrebbe fare un po' quello che fa Linux, ovvero ricopiare pari pari la directory e le tabelle delle pagine mettendole in protezione Copy-On-Write; il problema degli handle degli heap però rimane comunque... uqesta soluzione altro non sarebbe che una versione più efficiente della soluzione in cui uso VirtualAlloc per ricopiare gli heap nel processo figlio. |
|
|
|
|
|
|
#16 |
|
Senior Member
Iscritto dal: Jun 2002
Città: Dublin
Messaggi: 5989
|
O bella questa...
__________________
C'ho certi cazzi Mafa' che manco tu che sei pratica li hai visti mai! |
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 03:21.










) esiste una funzione analoga alla fork su Windows?"; dopodiché arriva un altro niubbo (un po' meno niubbo del precedente però) e fa "ah bla bla bla la fork è una prerogativa dei sistemi Unix ecc. ecc.".









