PDA

View Full Version : [Windows - qualsiasi] Accesso in lettura a files utilizzati da un altro processo


^TiGeRShArK^
19-02-2008, 18:01
Hola,
è possibile accedere in qualche modo in lettura a dei file lockati con delle API di "alto livello" o bisogna per forza "sporcarsi le mani" con qualche trucchetto un pò + a basso livello? :fagiano:

71104
19-02-2008, 18:29
potrebbe non essere possibile per nulla. questo è il prototipo della CreateFile: http://msdn2.microsoft.com/en-us/library/aa363858.aspx

come vedi dai flag del terzo parametro, è possibile riservarsi un file impedendo agli altri processi anche la lettura.

^TiGeRShArK^
19-02-2008, 18:39
potrebbe non essere possibile per nulla. questo è il prototipo della CreateFile: http://msdn2.microsoft.com/en-us/library/aa363858.aspx

come vedi dai flag del terzo parametro, è possibile riservarsi un file impedendo agli altri processi anche la lettura.
si.. infatti lo temevo anch'io per le API ad alto livello...
Ma a livello di accesso a basso livello dei blocchi sul disco non dovrebbe essere possibile farlo?
Qualcuno sa da che parte iniziare per implementare qualcosa di simile in .Net? :fagiano:

71104
19-02-2008, 19:02
Ma a livello di accesso a basso livello dei blocchi sul disco non dovrebbe essere possibile farlo? non avendo le specifiche ufficiali di NTFS direi che è abbastanza impossibile :fagiano:
o se ti vuoi fidare di quelle ricostruite "ad intuito" dal team di LinuxNTFS... :asd:

può darsi che ci siano altre vie prima di arrivare alla lettura manuale dei settori, ma di sicuro non in user-mode; a meno che il file non sia bloccato in lettura (cioè a meno che chi l'ha aperto abbia avuto la magnanimità di specificare FILE_SHARE_READ; tu hai la certezza che non l'abbia fatto?).

^TiGeRShArK^
19-02-2008, 19:48
non avendo le specifiche ufficiali di NTFS direi che è abbastanza impossibile :fagiano:
o se ti vuoi fidare di quelle ricostruite "ad intuito" dal team di LinuxNTFS... :asd:

può darsi che ci siano altre vie prima di arrivare alla lettura manuale dei settori, ma di sicuro non in user-mode; a meno che il file non sia bloccato in lettura (cioè a meno che chi l'ha aperto abbia avuto la magnanimità di specificare FILE_SHARE_READ; tu hai la certezza che non l'abbia fatto?).
si che ho la certezza perchè sto usando proprio il read only mode per aprire il file :D
cmq mi sa che lascio perdere e trovo un'altra soluzione perchè ho intravisto come fare sul blog di uno che ha scritto delle API per il defrag in C# e ha wrappato pure a mia nonna utilizzando chiamate native a kernel32.dll e mi è venuto il mal di testa a leggere quel codice :fagiano:
Se in caso devo per forza unlockare il file me lo studio con calma...
Ma per ora è yagni mi sa :p
ecco il codice che ho trovato giusto per info....

//
// a set of simple C# wrappers over the NT Defragmenter APIs
//

//
// Refrences

//
// http://www.sysinternals.com/ntw2k/info/defrag.shtml
//
// msdn how-to
// ms-help://MS.MSDNQTR.2004JUL.1033/fileio/base/defragmenting_files.htm
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/defragmenting_files.asp
//
// FSCTL_GET_VOLUME_BITMAP
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/fsctl_get_volume_bitmap.asp
//
// interesting structures...
// FSCTL_MOVE_FILE
// FSCTL_GET_RETRIEVAL_POINTERS
// RETRIEVAL_POINTERS_BUFFER
// FSCTL_GET_RETRIEVAL_POINTERS
//
// DeviceIoControl
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/deviceiocontrol.asp
//

using System;
using System.Diagnostics;
using System.Collections;
using System.Runtime.InteropServices;

namespace defraglib
{
public class IOWrapper
{

//
// CreateFile constants
//
const uint FILE_SHARE_READ = 0x00000001;
const uint FILE_SHARE_WRITE = 0x00000002;
const uint FILE_SHARE_DELETE = 0x00000004;
const uint OPEN_EXISTING = 3;

const uint GENERIC_READ = (0x80000000);
const uint GENERIC_WRITE = (0x40000000);

const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
const uint FILE_READ_ATTRIBUTES = (0x0080);
const uint FILE_WRITE_ATTRIBUTES = 0x0100;
const uint ERROR_INSUFFICIENT_BUFFER = 122;

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
static extern int CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
[Out] IntPtr lpOutBuffer,
uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);

static private IntPtr OpenVolume(string DeviceName)
{
IntPtr hDevice;
hDevice = CreateFile(
@"\\.\" + DeviceName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
0,
IntPtr.Zero);
if ((int)hDevice == -1)
{
throw new Exception(Marshal.GetLastWin32Error().ToString());
}
return hDevice;
}

static private IntPtr OpenFile(string path)
{
IntPtr hFile;
hFile = CreateFile(
path,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
0,
IntPtr.Zero);
if ((int)hFile == -1)
{
throw new Exception(Marshal.GetLastWin32Error().ToString());
}
return hFile;
}


/// <summary>
/// Get cluster usage for a device
/// </summary>
/// <param name="DeviceName">use "c:"</param>
/// <returns>a bitarray for each cluster</returns>
static public BitArray GetVolumeMap(string DeviceName)
{
IntPtr pAlloc = IntPtr.Zero;
IntPtr hDevice = IntPtr.Zero;

try
{
hDevice = OpenVolume(DeviceName);

Int64 i64 = 0;

GCHandle handle = GCHandle.Alloc(i64, GCHandleType.Pinned);
IntPtr p = handle.AddrOfPinnedObject();

// alloc off more than enough for my machine
// 64 megs == 67108864 bytes == 536870912 bits == cluster count
// NTFS 4k clusters == 2147483648 k of storage == 2097152 megs == 2048 gig disk storage
uint q = 1024 * 1024 * 64; // 1024 bytes == 1k * 1024 == 1 meg * 64 == 64 megs

uint size = 0;
pAlloc = Marshal.AllocHGlobal((int)q);
IntPtr pDest = pAlloc;

bool fResult = DeviceIoControl(
hDevice,
FSConstants.FSCTL_GET_VOLUME_BITMAP,
p,
(uint)Marshal.SizeOf(i64),
pDest,
q,
ref size,
IntPtr.Zero);

if (!fResult)
{
throw new Exception(Marshal.GetLastWin32Error().ToString());
}
handle.Free();

/*
object returned was...
typedef struct
{
LARGE_INTEGER StartingLcn;
LARGE_INTEGER BitmapSize;
BYTE Buffer[1];
} VOLUME_BITMAP_BUFFER, *PVOLUME_BITMAP_BUFFER;
*/
Int64 StartingLcn = (Int64)Marshal.PtrToStructure(pDest, typeof(Int64));

Debug.Assert(StartingLcn == 0);

pDest = (IntPtr)((Int64)pDest + 8);
Int64 BitmapSize = (Int64)Marshal.PtrToStructure(pDest, typeof(Int64));

Int32 byteSize = (int)(BitmapSize / 8);
byteSize++; // round up - even with no remainder

IntPtr BitmapBegin = (IntPtr)((Int64)pDest + 8);

byte[] byteArr = new byte[byteSize];

Marshal.Copy(BitmapBegin, byteArr, 0, (Int32)byteSize);

BitArray retVal = new BitArray(byteArr);
retVal.Length = (int)BitmapSize; // truncate to exact cluster count
return retVal;
}
finally
{
CloseHandle(hDevice);
hDevice = IntPtr.Zero;

Marshal.FreeHGlobal(pAlloc);
pAlloc = IntPtr.Zero;
}
}

/// <summary>
/// returns a 2*number of extents array -
/// the vcn and the lcn as pairs
/// </summary>
/// <param name="path">file to get the map for ex: "c:\windows\explorer.exe" </param>
/// <returns>An array of [virtual cluster, physical cluster]</returns>
static public Array GetFileMap(string path)
{
IntPtr hFile = IntPtr.Zero;
IntPtr pAlloc = IntPtr.Zero;

try
{
hFile = OpenFile(path);

Int64 i64 = 0;

GCHandle handle = GCHandle.Alloc(i64, GCHandleType.Pinned);
IntPtr p = handle.AddrOfPinnedObject();

uint q = 1024 * 1024 * 64; // 1024 bytes == 1k * 1024 == 1 meg * 64 == 64 megs

uint size = 0;
pAlloc = Marshal.AllocHGlobal((int)q);
IntPtr pDest = pAlloc;
bool fResult = DeviceIoControl(
hFile,
FSConstants.FSCTL_GET_RETRIEVAL_POINTERS,
p,
(uint)Marshal.SizeOf(i64),
pDest,
q,
ref size,
IntPtr.Zero);

if (!fResult)
{
throw new Exception(Marshal.GetLastWin32Error().ToString());
}

handle.Free();

/*
returned back one of...
typedef struct RETRIEVAL_POINTERS_BUFFER {
DWORD ExtentCount;
LARGE_INTEGER StartingVcn;
struct {
LARGE_INTEGER NextVcn;
LARGE_INTEGER Lcn;
} Extents[1];
} RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER;
*/

Int32 ExtentCount = (Int32)Marshal.PtrToStructure(pDest, typeof(Int32));

pDest = (IntPtr)((Int64)pDest + 4);

Int64 StartingVcn = (Int64)Marshal.PtrToStructure(pDest, typeof(Int64));

Debug.Assert(StartingVcn == 0);

pDest = (IntPtr)((Int64)pDest + 8);

// now pDest points at an array of pairs of Int64s.

Array retVal = Array.CreateInstance(typeof(Int64), new int[2] { ExtentCount, 2 });

for (int i = 0; i < ExtentCount; i++)
{
for (int j = 0; j < 2; j++)
{
Int64 v = (Int64)Marshal.PtrToStructure(pDest, typeof(Int64));
retVal.SetValue(v, new int[2] { i, j });
pDest = (IntPtr)((Int64)pDest + 8);
}
}

return retVal;
}
finally
{
CloseHandle(hFile);
hFile = IntPtr.Zero;

Marshal.FreeHGlobal(pAlloc);
pAlloc = IntPtr.Zero;
}
}

/// <summary>
/// input structure for use in MoveFile
/// </summary>
private struct MoveFileData
{
public IntPtr hFile;
public Int64 StartingVCN;
public Int64 StartingLCN;
public Int32 ClusterCount;
}

/// <summary>
/// move a virtual cluster for a file to a logical cluster on disk, repeat for count clusters
/// </summary>
/// <param name="deviceName">device to move on"c:"</param>
/// <param name="path">file to muck with "c:\windows\explorer.exe"</param>
/// <param name="VCN">cluster number in file to move</param>
/// <param name="LCN">cluster on disk to move to</param>
/// <param name="count">for how many clusters</param>
static public void MoveFile(string deviceName, string path, Int64 VCN, Int64 LCN, Int32 count)
{
IntPtr hVol = IntPtr.Zero;
IntPtr hFile = IntPtr.Zero;
try
{
hVol = OpenVolume(deviceName);

hFile = OpenFile(path);


MoveFileData mfd = new MoveFileData();
mfd.hFile = hFile;
mfd.StartingVCN = VCN;
mfd.StartingLCN = LCN;
mfd.ClusterCount = count;

GCHandle handle = GCHandle.Alloc(mfd, GCHandleType.Pinned);
IntPtr p = handle.AddrOfPinnedObject();
uint bufSize = (uint)Marshal.SizeOf(mfd);
uint size = 0;

bool fResult = DeviceIoControl(
hVol,
FSConstants.FSCTL_MOVE_FILE,
p,
bufSize,
IntPtr.Zero, // no output data from this FSCTL
0,
ref size,
IntPtr.Zero);

handle.Free();

if (!fResult)
{
throw new Exception(Marshal.GetLastWin32Error().ToString());
}
}
finally
{
CloseHandle(hVol);
CloseHandle(hFile);
}
}
}


/// <summary>
/// constants lifted from winioctl.h from platform sdk
/// </summary>
internal class FSConstants
{
const uint FILE_DEVICE_FILE_SYSTEM = 0x00000009;

const uint METHOD_NEITHER = 3;
const uint METHOD_BUFFERED = 0;

const uint FILE_ANY_ACCESS = 0;
const uint FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS;

public static uint FSCTL_GET_VOLUME_BITMAP = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 27, METHOD_NEITHER, FILE_ANY_ACCESS);
public static uint FSCTL_GET_RETRIEVAL_POINTERS = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 28, METHOD_NEITHER, FILE_ANY_ACCESS);
public static uint FSCTL_MOVE_FILE = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 29, METHOD_BUFFERED, FILE_SPECIAL_ACCESS);

static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)
{
return ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method);
}
}

}

71104
19-02-2008, 19:55
si che ho la certezza perchè sto usando proprio il read only mode per aprire il file :D AAAAAAAAAAAA e non avevo capito :D

be' ma scusa in .NET puro, senza wrappare le Win32, non si può già aprire un file in sola lettura? ho poca esperienza purtroppo, ho scritto solo qualche programmetto in C#, ma su MSDN vedo che per esempio la classe FileStream permette di specificare modalità di accesso ed algoritmi di apertura praticamente identici alle loro controparti Win32:
http://msdn2.microsoft.com/en-us/library/system.io.filestream.filestream.aspx

http://msdn2.microsoft.com/en-us/library/4z36sx0f.aspx
http://msdn2.microsoft.com/en-us/library/system.io.filemode.aspx

^TiGeRShArK^
19-02-2008, 21:07
AAAAAAAAAAAA e non avevo capito :D

be' ma scusa in .NET puro, senza wrappare le Win32, non si può già aprire un file in sola lettura? ho poca esperienza purtroppo, ho scritto solo qualche programmetto in C#, ma su MSDN vedo che per esempio la classe FileStream permette di specificare modalità di accesso ed algoritmi di apertura praticamente identici alle loro controparti Win32:
http://msdn2.microsoft.com/en-us/library/system.io.filestream.filestream.aspx

http://msdn2.microsoft.com/en-us/library/4z36sx0f.aspx
http://msdn2.microsoft.com/en-us/library/system.io.filemode.aspx
Si, però mi sa che il file è lockato in modalità none non in modalita share.read :p
Cmq avevo visto in qualche posto che basterebbe usare un'opzione backup.read o qualcosa del genere nelle API a basso livello per aprire il file...
Ma sinceramente wrappare mezzo mondo, usare quest'opzione e accedere a codice non-managed non mi pare proprio bellissimo da fare :fagiano:
Quindi mi sa che utilizzerò la famosa tecnica dello struzzo per risolvere il problema :p

eraser
20-02-2008, 00:21
Hola,
è possibile accedere in qualche modo in lettura a dei file lockati con delle API di "alto livello" o bisogna per forza "sporcarsi le mani" con qualche trucchetto un pò + a basso livello? :fagiano:

È possibile in kernel mode, senza andare a sporcarsi le mani con un NTFS parser che non è sicuramente immediato da scrivere.
In entrambi i casi non è comunque immediato, se puoi evita.

gugoXX
20-02-2008, 00:33
Se puoi lanciare una shell esterna e se ti va di farlo, ho un eseguibile che permette da linea di comando di liberare un file completamente.
Anche quelli caricati in memoria, non solo quelli aperti in lettura/scrittura da altri.
Dicono funzioni anche per kernel.exe, volendo puoi cancellarlo.
Ovviamente non ho provato, se vuole provare qualcuno poi mi dice se c'e' riuscito...

71104
20-02-2008, 01:10
(cioè a meno che chi l'ha aperto abbia avuto la magnanimità di specificare FILE_SHARE_READ; tu hai la certezza che non l'abbia fatto?).

si che ho la certezza perchè sto usando proprio il read only mode per aprire il file :D

Si, però mi sa che il file è lockato in modalità none non in modalita share.read :p
ehm, c'è qualcosa che non va :fagiano:

71104
20-02-2008, 01:12
ehm, c'è qualcosa che non va :fagiano: ehm, asd, ho riletto meglio: il qualcosa che non andava ero io :asd:
c'era quel non che mi incasinava un po' :fagiano:

bene bene, oggi posso definitivamente affermare di essere totalmente rincoglionito: per la terza volta in poche ore mi vedo costretto a domandare umilmente scusa per le castronerie che dico su HWU :mc:

ribadisco: ho avuto una giornataccia -.-'

71104
20-02-2008, 01:16
È possibile in kernel mode, ci stavo riflettendo... come si potrebbe fare? :wtf:
servirebbe un file system filter?

ho una discreta esperienza con la storage architecture ma quasi niente coi FSD.

senza andare a sporcarsi le mani con un NTFS parser che non è sicuramente immediato da scrivere. ah be' quello per quanto mi riguarda è impossibile: se le specifiche ufficiali non esistono ciccia, io al posto di TigerShark non andrei ad impazzire sulle pippe mentali di LinuxNTFS neanche se mi pagassero; o perlomeno dovrebbero pagarmi tanto.

eraser
20-02-2008, 01:18
Si, è necessario un FSD e Windows viene in aiuto (da XP in poi).

Per quanto riguarda un ntfs parser, non è impossibile ma sicuramente non è immediato. È possibile e funzionerebbe ovviamente semplicemente da user mode.

71104
20-02-2008, 01:20
Se puoi lanciare una shell esterna e se ti va di farlo, lanciare una console esterna non credo sia un problema: al limite basta che nel creare il processo redirige gli standard streams su una pipe, anche se poi non la usa; purché la pipe abbia abbastanza buffer da non far bloccare il programma per un banale output.

Dicono funzioni anche per kernel.exe, volendo puoi cancellarlo. volevi dire ntoskrnl.exe?

71104
20-02-2008, 01:20
Si, è necessario un FSD e Windows viene in aiuto (da XP in poi). perché prima che succedeva?

eraser
20-02-2008, 01:23
Da XP in poi sono presenti delle API native che permettono di ignorare gli share-access checks (poi migliorate in Vista).

71104
20-02-2008, 02:42
Da XP in poi sono presenti delle API native che permettono di ignorare gli share-access checks (poi migliorate in Vista). API native, cioè quelle che iniziano con Zw ma anche Nt? da quanto mi risulta quelle si possono usare anche in user-mode, quindi costituirebbero la soluzione per TigerShark. sono documentate?

eraser
20-02-2008, 02:51
No, sono API esportate dal kernel (Io*) utilizzabili esclusivamente lato kernel mode, in un FSD.

^TiGeRShArK^
20-02-2008, 13:16
Se puoi lanciare una shell esterna e se ti va di farlo, ho un eseguibile che permette da linea di comando di liberare un file completamente.
Anche quelli caricati in memoria, non solo quelli aperti in lettura/scrittura da altri.
Dicono funzioni anche per kernel.exe, volendo puoi cancellarlo.
Ovviamente non ho provato, se vuole provare qualcuno poi mi dice se c'e' riuscito...
non penso che sia quello che mi serve, perchè se il programma fa una cosa del genere immagino che "rubi" l'handle al processo che lo sta utilizzando, mentre io invece voglio cheil processo che lo sta utilizzando continui a fare indisturbato quello che sta facendo.... :p

^TiGeRShArK^
20-02-2008, 13:20
No, sono API esportate dal kernel (Io*) utilizzabili esclusivamente lato kernel mode, in un FSD.
Io ieri avevo trovato questo:

Basically you need to open the file using Win32 "CreateFile" API with:
dwDesiredACcess = 0
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE
and dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS set.
Once you have the file handle, you can use it to perform backup operations using the Backup API's like Kernel32 API "BackupRead".
Nothing of this all is wrapped by the Framework, you need to PInvoke. Also, you need to run with the "SeBackupPrivilege "enabled for your process, (IMO) something you don't want your user processes to run with.

Che, se non ho capito male, potrebbe andare in user-mode...
Ma sinceramente il rapporto costi/benefici di andare ad implementare tutto questo bel casino (e soprattutto il testarlo) non è a suo favore, quindi per ora ho deciso semplicemente di ignorare il problema...

In caso + avanti mi rendo conto che è assolutamente necessario allora torno a sbatterci al testa..
grazie a tutti per l'aiuto cmq :D

eraser
20-02-2008, 13:30
Io ieri avevo trovato questo:

Che, se non ho capito male, potrebbe andare in user-mode...
Ma sinceramente il rapporto costi/benefici di andare ad implementare tutto questo bel casino (e soprattutto il testarlo) non è a suo favore, quindi per ora ho deciso semplicemente di ignorare il problema...


Potrebbe, dipende.

Invece da kernel mode puoi, senza il condizionale :D Tutto qua :)

Un'idea stupida, ad esempio (non so che cosa ci devi fare, per cui puoi anche tralasciare l'idea e riderci su) potrebbe essere quella di lockare il file prima che venga lockato dall'applicazione in questione. Così ti guadagni l'accesso.

Non è un'opzione ovviamente sempre valida, anzi tutt'altro. Dipende dai tipi di applicazioni.

^TiGeRShArK^
20-02-2008, 14:09
Potrebbe, dipende.

Invece da kernel mode puoi, senza il condizionale :D Tutto qua :)

Un'idea stupida, ad esempio (non so che cosa ci devi fare, per cui puoi anche tralasciare l'idea e riderci su) potrebbe essere quella di lockare il file prima che venga lockato dall'applicazione in questione. Così ti guadagni l'accesso.

Non è un'opzione ovviamente sempre valida, anzi tutt'altro. Dipende dai tipi di applicazioni.

nel mio caso non andrebbe bene perchè la mia applicazione deve essere totalmente trasparente per gli altri processi :p

eraser
20-02-2008, 14:11
nel mio caso non andrebbe bene perchè la mia applicazione deve essere totalmente trasparente per gli altri processi :p

ottimo :)

gugoXX
20-02-2008, 14:20
forse ho capito cosa ti serve.
Un'altra idea ce l'avrei.

Potresti utilizzare il trampolining sulle API, che e' un po' come l'hooking ma e' piu' cattivo.
Potresti mettere sotto trampolining CreateFile, ReadFile, WriteFile, OpenFile
In pratica ti sostituiresti temporaneamente al SO su ciascuna di queste funzioni, intercettando i flussi prima che vadano ai processi user (o appena usciti da essi)

Se devi intercettare in scrittura quello che fa un altro processo ti basterebbe sapere quel e' l'handle del file che ti interessa (e lo prendi dal trampoling della OpenFile o dalla Createfile), e poi ogni volta che viene chiamata la WriteFile controlli se l'handle e' quello che ti serve.
Se non lo e' continui come se tu non ci fossi, se invece lo e' ti memorizzi quanto l'utente vorrebbe scrivere in un tuo buffer, ad uso e consumo di altri tuoi processi.

Qualche mese fa ho anche trovato e provato una libereria che permetteva di gestire molto semplicemente il trampolining, senza preoccuparsi troppo di anelli, protezione, etc.etc.
Fammi sapere se vuoi andare da questa parte. E' sicuramente molto a basso livello.

71104
20-02-2008, 16:18
forse ho capito cosa ti serve.
Un'altra idea ce l'avrei.

Potresti utilizzare il trampolining sulle API, che e' un po' come l'hooking ma e' piu' cattivo.
Potresti mettere sotto trampolining CreateFile, ReadFile, WriteFile, OpenFile
In pratica ti sostituiresti temporaneamente al SO su ciascuna di queste funzioni, intercettando i flussi prima che vadano ai processi user (o appena usciti da essi)

Se devi intercettare in scrittura quello che fa un altro processo ti basterebbe sapere quel e' l'handle del file che ti interessa (e lo prendi dal trampoling della OpenFile o dalla Createfile), e poi ogni volta che viene chiamata la WriteFile controlli se l'handle e' quello che ti serve.
Se non lo e' continui come se tu non ci fossi, se invece lo e' ti memorizzi quanto l'utente vorrebbe scrivere in un tuo buffer, ad uso e consumo di altri tuoi processi.

Qualche mese fa ho anche trovato e provato una libereria che permetteva di gestire molto semplicemente il trampolining, senza preoccuparsi troppo di anelli, protezione, etc.etc.
Fammi sapere se vuoi andare da questa parte. E' sicuramente molto a basso livello.

tra tutti gli esperimenti che feci a suo tempo con le Detours il termine "trampolining" non l'avevo mai sentito :asd:

comunque se conosco abbastanza TigerShark l'hooking di funzioni è una porcheria alla quale non si abbasserà mai :p
per almeno i seguenti motivi:
1) non è esaustiva: CreateFileW non è l'ultimo tratto del percorso verso la richiesta di un servizio al kernel - e per cortesia non proponiamo soluzioni "alternative" come l'intercettazione della int 0x2E
2) non è quasi mai attuabile: richiede che l'altro processo carichi un tuo modulo eseguibile - e per cortesia non proponiamo soluzioni "creative" come la DLL injection
3) mette a rischio l'altro processo - per il motivo n° 2 ovviamente, nei casi in cui il modulo eseguibile in questione sia buggato

certe sporche pratiche messe in atto dalle Detours a mio parere dovrebbero essere finalizzate esclusivamente al debugging. a questo punto preferirei scrivere un driver, ma la cosa più sensata di tutte sarebbe semplicemente rinunciare a leggere quel file e trovare un'alternativa pulita, visto che se il file è bloccato un motivo ci sarà e di conseguenza qualunque soluzione è sporca (chi più chi meno).

gugoXX
20-02-2008, 16:58
Anche oggi ti sei svegliato male?
Avanti con la soluzione pulita per questo problema allora.

71104
20-02-2008, 17:03
e leggi...

gugoXX
20-02-2008, 17:13
e leggi...
Non capisco. Non mi sembra una frase di senso compiuto.

Comunque giusto per essere proattivo, che serva per questo caso o no, quoto qui un pezzo di documento preso dalla Intel Software Network.
Come gia' detto e' una tecnica abbastanza a basso livello, da utilizzarsi quando non vengono altre idee.

Trampoline Function
In many cases, the replacement function needs to call the original target function in addition to its own code, in order to extend the capability of the API, rather than replacing the whole thing. A trampoline function provides this functionality. The theory behind trampoline functions is as follows:


Prepare a dummy function that has the same declaration that will be used as the trampoline. Make sure the dummy function is more than 10 bytes long.


Before overwriting the first five bytes of the target function, copy them to the beginning of the trampoline function.


Overwrite from the sixth byte of the trampoline with an unconditional jump to the sixth byte of the target function


Overwrite the target function as before.


When a trampoline function is called (from the replacement function or anywhere else), it executes the first five bytes of the copied original code, and then jumps to the sixth byte of the real original code. The control returns to the caller of the trampoline. After optionally completing additional tasks, control returns to the caller of the API.



One additional complication exists, in that the sixth byte of the original code may be part of the previous instruction. In that case, the function overwrites part of the previous instruction and then crashes. In the case of GetSystemPowerStatus, the beginning of a new instruction after the first five bytes is the seventh byte. Thus, for this scheme to work, six bytes need to be copied to the trampoline, and the code must adjust this offset accordingly.

The number of bytes that the code needs to copy depends upon the API. It is necessary to look at the original target code (using a debugger or a disassembler) and to count the number of bytes to copy. A future enhancement could automatically detect the correct offset. Assuming that we know the correct offset, the following code shows the extended InterceptAPI function that sets up the trampoline function as well:


Come si puo' vedere e' una porcata, ma non si tratta di andare a leggere dati da file lockati, ne' che il processo da monitorare carichi un tuo modulo eseguibile.
Certo, se quello che si fa si fa male c'e' il rischio di compromettere il funzionamento del modulo sensorizzato.

PS: Non mi sognerei mai di attuare una tecnica simile per qualcosa che esca fuori da casa mia. Tutto sta nel sapere quali siano gli intenti e i bisogni di TigerShark, cosa che non mi e' ancora del tutto chiara.

71104
20-02-2008, 17:21
Come si puo' vedere e' una porcata, ma non si tratta di andare a leggere dati da file lockati, che è quello che servirebbe invece a TigerShark.

ne' che il processo da monitorare carichi un tuo modulo eseguibile. dovrà farlo invece se vorrà eseguire il tuo codice, a meno che non hai intenzione di scrivercelo a mano con WriteProcessMemory, ipotesi non ragionevolmente praticabile nel caso che il tuo codice debba essere rilocato.

gugoXX
20-02-2008, 17:28
che è quello che servirebbe invece a TigerShark.
Intendevo dire che non andrebbe a leggere fisicamente i dati dai file, lockati o meno che siano. Potrebbe semplicemente tenere copia in memoria dei dati che il processo utente andrebbe normalmente a scrivere come se nulla stesse succedendo.


dovrà farlo invece se vorrà eseguire il tuo codice, a meno che non hai intenzione di scrivercelo a mano con WriteProcessMemory, ipotesi non ragionevolmente praticabile nel caso che il tuo codice debba essere rilocato.
E' sufficiente che il modulo che implementa il trampoling sia a se stante, caricato quando pare e piace purche' prima delle chiamate del programma da monitorare.

Comunque abbiamo capito tutti che non ti piace, solo non puoi dire che non stia in piedi.

eraser
20-02-2008, 20:07
Qualche mese fa ho anche trovato e provato una libereria che permetteva di gestire molto semplicemente il trampolining, senza preoccuparsi troppo di anelli, protezione, etc.etc.
Fammi sapere se vuoi andare da questa parte. E' sicuramente molto a basso livello.

Penso tu ti riferisca al madCodeHook di Madshi, utilizzato spesso da malware e di conseguenza non più liberamente scaricabile dal sito web.

Non è "molto a basso livello" :) È una tecnica di inline hooking tutt'ora utilizzata da molti rootkit user mode. È anche bypassabile abbastanza agevolmente tuttavia.

Per il caricamento del modulo di hooking tra i moduli dei processi potresti anche utilizzare Windows direttamente invece di un tuo loader.

Più che altro la sconsiglierei perché non è ottimale come soluzione, soprattutto perché richiede una complessità di sviluppo non proporzionata all'azione in sé (in altre parole è come se per andare da Firenze a Pisa passi per Milano).

Per quanto riguarda lo sviluppo di un driver, a questo punto si scende già più a basso livello e, se ci pensate bene, c'è un componente di Windows che potrebbe avere bisogno di accedere a file lockati e proprio quel componente utilizza le API di cui per l'appunto parlavo ieri.

Pulita come cosa, però richiede altra complessità di sviluppo che penso sia fuori dal livello di complessità che l'autore del thread voleva raggiungere al momento.

71104
20-02-2008, 20:59
E' sufficiente che il modulo che implementa il trampoling sia a se stante, caricato quando pare e piace purche' prima delle chiamate del programma da monitorare. la DLL contenente la tua funzione di intercettazione deve stare nell'altro processo -- punto.

Comunque abbiamo capito tutti che non ti piace, solo non puoi dire che non stia in piedi. funziona solo in casi particolari, ovvero quando:

il programma di cui vuoi intercettare certe chiamate è una normale applicazione Win32, ed inoltre
sei tu a crearne il processo oppure
non effettua contemporaneamente da thread diversi chiamate alla stessa routine da intercettare, ed inoltre
dispone di un suo sistema per caricare DLL esterne, oppure
hai sul suo processo abbastanza permessi da poterci allocare un nuovo thread e
l'altro processo ha i permessi necessari per accedere al file della tua DLL, oppure ancora
l'altro processo ha un message loop e tu riesci ad installargli un Windows Hook non Low Level, oppure ancora
hai sul processo abbastanza permessi da poterci allocare un nuovo thread e pure nuove zone di memoria virtuale, nonché il tempo e la pazienza di scrivere codice di rilocamento per le routines che vai a scrivere manualmente in un altro spazio di indirizzamento.

infine prega di non aver introdotto bugs nello scrivere la DLL o le routines di intercettazione o quello che è.

^TiGeRShArK^
21-02-2008, 17:23
opss.. :stordita:
non credevo di scatenare questo putiferio :asd:
Cmq la soluzione degli hook mi pare così ad occhio troppo macchinosa e soprattutto "pericolosa".
Con la mia applicazione non voglio andare assolutamente ad intaccare il sistema (anche per questo ho provvisoriamente scartato il kernel mode) e in quel modo sarebbe troppo "invasiva" :p