Recentemente, in this question, ho chiesto come ottenere un indirizzo di memoria raw di classe in C# (è un rozzo hack inaffidabile e una cattiva pratica, non usarlo a meno che non ne abbia davvero bisogno). Sono riuscito, ma poi è sorto un problema: secondo l'articolo this, le prime 2 parole nella rappresentazione della memoria grezza della classe dovrebbero essere puntatori alle strutture SyncBlock e RTTI e quindi l'indirizzo del primo campo deve essere sfalsato di 2 parole [8 byte in sistemi a 32 bit, 16 byte in sistemi a 64 bit] dall'inizio. Tuttavia, quando eseguo il dump dei primi byte dalla memoria nella posizione dell'oggetto, l'offset grezzo del primo campo dall'indirizzo dell'oggetto è solo 1 parola a 32 bit (4 byte), che non ha alcun senso per entrambi i tipi di sistemi . Dalla domanda che ho collegato:Quali sono il layout e le dimensioni dell'intestazione di una classe gestita nella memoria non gestita?
class Program
{
// Here is the function.
// I suggest looking at the original question's solution, as it is
// more reliable.
static IntPtr getPointerToObject(Object unmanagedObject)
{
GCHandle gcHandle = GCHandle.Alloc(unmanagedObject, GCHandleType.WeakTrackResurrection);
IntPtr thePointer = Marshal.ReadIntPtr(GCHandle.ToIntPtr(gcHandle));
gcHandle.Free();
return thePointer;
}
class TestClass
{
uint a = 0xDEADBEEF;
}
static void Main(string[] args)
{
byte[] cls = new byte[16];
var test = new TestClass();
var thePointer = getPointerToObject(test);
Marshal.Copy(thePointer, cls, 0, 16); //Dump first 16 bytes...
Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(thePointer.ToInt32())));
Console.WriteLine(BitConverter.ToString(cls));
Console.ReadLine();
gcHandle.Free();
}
}
/* Example output (yours should be different):
40-23-CA-02
4C-38-04-01-EF-BE-AD-DE-00-00-00-80-B4-21-50-73
That field's value is "EF-BE-AD-DE", 0xDEADBEEF as it is stored in memory. Yay, we found it!
*/
Perché è così? Forse ho sbagliato l'indirizzo, ma come e perché? E se non l'avessi fatto, cosa potrebbe esserci di sbagliato comunque? Forse, se quell'articolo è sbagliato, ho semplicemente frainteso l'aspetto dell'intestazione di classe gestita? O forse non ha quel puntatore di blocco - ma perché e come è possibile? ..
(Queste sono, ovviamente, solo alcune opzioni possibili, e, mentre sto ancora andando a controllare attentamente ciascuna Posso prevedere che l'ipotesi selvaggia non può essere paragonata in termini di tempo e accuratezza a una risposta corretta)
Il puntatore dell'oggetto punta al secondo campo nell'intestazione dell'oggetto. Un trucco per mantenere il codice macchina più compatto, è quello che è più probabile che venga utilizzato. Tieni presente che il codice è drasticamente sbagliato, viene generato un disastro quando il garbage collector viene eseguito dopo la chiamata getPtrToObject(). Probabilità basse, non zero. Appuntare è un requisito difficile. –
@HansPassant L'indice SyncRoot non dovrebbe essere in offset -4? –
Sì, il primo campo è quindi un indice negativo. –