HWBP on access

06/23/2012 00:30 Lazeboy#1
Hey
ich wollte mal fragen ob jemand weiss wie ich harware bp on access setze. Zurzeit benutze ich normale hardware bp welche wahrscheinlich den hwbp on execution ensprechen.

Code:
LONG WINAPI UnhandlerExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
		if(ExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_SINGLE_STEP )
	{
		
		if ((DWORD)ExceptionInfo->ExceptionRecord->ExceptionAddress==GetTickCountAddr)
		{
			
			return EXCEPTION_CONTINUE_EXECUTION;
		}
	}

	return EXCEPTION_CONTINUE_SEARCH;
}
Welche Exception muss ich nehmen um die anderen arten von hwbp zu setzen ?

mfg Lazeboy
06/23/2012 01:26 Nightblizard#2
Was heißt "on access"? Du kannst zwischen execute, write und readwrite wählen und das steuerst du nicht über die Exception, sondern über die Bits 16-23 (jeh nach Debug-Register) des Dr7-Registers.
execute = 0
write = 1
readwrite = 3
06/23/2012 11:30 Lazeboy#3
Cool danke habe nur noch eine kleine Fragen.

1: wird bei readwrite immer noch die Single step exception ausgeführt?
06/23/2012 12:33 MrSm!th#4
Quote:
sondern über die Bits 16-23 (jeh nach Debug-Register) des Dr7-Registers.
Und wie speichert man in 7 Bit die Typen von 4 HWBPs, wenn die Werte 2 Bit Platz benötigen? oO
06/23/2012 16:45 Ende!#5
Quote:
Originally Posted by Lazeboy View Post
1: wird bei readwrite immer noch die Single step exception ausgeführt?
Ja

Quote:
Originally Posted by MrSm!th View Post
Und wie speichert man in 7 Bit die Typen von 4 HWBPs, wenn die Werte 2 Bit Platz benötigen? oO
Das mit dem Zählen üben wir aber nochmal! :p

16 <- 1
17 <- 2
18 <- 3
19 <- 4
20 <- 5
21 <- 6
22 <- 7
23 <- 8
06/24/2012 20:37 MrSm!th#6
Hab einfach Minus gerechnet, Zählen is mir zu mainstream.

Ich lieg aber richtig damit, dass quasi jeder HWBP 2 der Bits hat?
Ist dann sicherlich big-endian oder?
06/24/2012 20:53 Ende!#7
Quote:
Originally Posted by MrSm!th View Post
Ich lieg aber richtig damit, dass quasi jeder HWBP 2 der Bits hat?
Ist dann sicherlich big-endian oder?
Seit wann muss man sich bei Registern Sorgen um die Endianness machen? Das ist doch nur bei Speicherzugriffen der Fall, bei denen man auf eine andere Anzahl von Bytes zugreift, als als der Wert abgelegt wurde.

Ansonsten aber korrekt, siehe auch Intel Doc:
[Only registered and activated users can see links. Click Here To Register...]
06/24/2012 20:57 MrSm!th#8
Streng genommen kann es Endianness auch bei Registern geben, es ist ein Unterschied ob 10 1 oder 2 bedeutet ;O
Wollte nur nochmal sichergehen.

Wofür steht denn das LEN?
06/24/2012 21:10 Ende!#9
Quote:
Originally Posted by MrSm!th View Post
Streng genommen kann es Endianness auch bei Registern geben, es ist ein Unterschied ob 10 1 oder 2 bedeutet ;O
Könnte, gibt es in der x86er Architektur aber nicht.
Quote:
Originally Posted by MrSm!th View Post
Wofür steht denn das LEN?
Für die Anzahl der Bytes, für die der Breakpoint gilt.

00 <- 1 byte
01 <- 2 byte
10 <- undefiniert (bzw im x86_64er Modus 8 byte)
11 <- 4 byte

Das ist nur bei Write-/ReadWrite-Breakpoints möglich, das Verhalten beim Setzen eines anderen Wertes als 00 ruft bei einem Execute-Breakpoint undefiniertes Verhalten hervor.
06/24/2012 22:35 MrSm!th#10
Aber dann sind es ja doch nicht die Bits 16-23 sondern 16-31, wovon aber die eine Hälfte die Längen und die andere die Typen speichert.

Was ich mit Endianness auch meinte (und nur falsch ausgedrückt habe) war auch die Reihenfolge der BPs in diesen Bits, aber das hat ja dein Bild aufgeklärt.
06/24/2012 22:42 Ende!#11
Quote:
Originally Posted by MrSm!th View Post
Aber dann sind es ja doch nicht die Bits 16-23 sondern 16-31, wovon aber die eine Hälfte die Längen und die andere die Typen speichert.
Ja, richtig. Ich hatte das nicht mehr im Kopf und hatte erstmal angenommen, dass Nightblizzard's Aussage korrekt ist und meinen Post darauf bezogen.
Quote:
Originally Posted by MrSm!th View Post
Was ich mit Endianness auch meinte (und nur falsch ausgedrückt habe) war auch die Reihenfolge der BPs in diesen Bits, aber das hat ja dein Bild aufgeklärt.
Ah, so wird die Frage natürlich verständlich.
06/25/2012 01:53 Nightblizard#12
Quote:
Originally Posted by Ende! View Post
Ja, richtig. Ich hatte das nicht mehr im Kopf und hatte erstmal angenommen, dass Nightblizzard's Aussage korrekt ist und meinen Post darauf bezogen.
Oh, da hab ich meinen eigenen Code falsch gelesen... Das muss ich gleich umschreiben!

Um mich also selber nochmal zu verbessern:
16-31 - 2 Bits Typ gefolgt von 2 Bits Länge

Typ:
Code:
enum HardwareBreakpointType
{
	TYPE_EXECUTE = 0,
	TYPE_READ = 1,
	TYPE_IOREADWRITE = 2, //requires DE flag set in register CR4
	TYPE_READWRITE = 3,
};
Länge:
Code:
enum HardwareBreakpointSize
{
	SIZE_1 = 0,
	SIZE_2 = 1,
	SIZE_4 = 3,
	SIZE_8 = 2, //not supported by all CPUs
};
06/25/2012 13:00 Lazeboy#13
wenn ich ehrlich bin bin ich gerade verwirrt.. Also dx7 besteht aus 16 Bit die Hälfte dient zur Angabe der länge und die andere Hälfte gib die Art des bp an? Das setzen von dx7 macht mir dann aber Probleme. Ich denke ich hab irgendwie noch nicht alles verstanden.
06/25/2012 17:42 Ende!#14
Code:
// Fill destination address register corresponding to our slot with the
// address our BP is set on
switch(pBP->m_slot)
{
   case 0:  ctx.Dr0 = (dword)pBP->m_pTarget; break;
   case 1:  ctx.Dr1 = (dword)pBP->m_pTarget; break;
   case 2:  ctx.Dr2 = (dword)pBP->m_pTarget; break;
   case 3:  ctx.Dr3 = (dword)pBP->m_pTarget; break;
}

// Set size
switch (pBP->m_size)
{
case (SizeByte):
   // Skip, is already 0!
   break;
case (SizeWord):
   ctx.Dr7 |= (1 << (18 + 4 * pBP->m_slot));
   break;
case (SizeDword):
   ctx.Dr7 |= (2 << (18 + 4 * pBP->m_slot));
}

// X/W/RW?
switch (pBP->m_condition)
{
case (TriggerOnExecute):
   // Nothing to do, is already zero
   break;
case (TriggerOnWrite):
   ctx.Dr7 |= (1 << (16 + 4 * pBP->m_slot));
   break;
case (TriggerOnReadWrite):
   ctx.Dr7 |= (2 << (16 + 4 * pBP->m_slot));
}

// Enable BP
if (pBP->m_isEnabled)
   ctx.Dr7 |= (1 << (2 * pBP->m_slot));
Copy&Pasta aus meinem Framework - eventuell hilft dir das beim Verstehen.

SizeWord, TriggerOnExecute usw. sind halt Werte aus Enumeratoren - du könntest dir die Hälfte der Switches auch absparen und es mit den Enums so lösen, wie Nightblizzard es tut, mir persönlich gefällt die strong-typed Variante aber besser.
06/25/2012 19:10 link#15
Quote:
Originally Posted by Ende! View Post
[Only registered and activated users can see links. Click Here To Register...]
Wenn du z.B. einen Hardware Breakpoint auf den Befehl x an Adresse 401488h setzen willst, funktioniert das so:

Quote:
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
Context.Dr0 = 0x401488;
Context.Dr7 |= 0x00000001; // or Dr7, 00 00 00 00 00 00 00 00 00000000 0 0 0 0 0 0 0 1 b
SetThreadContext(ThreadId, &Context);
LEN3R/W3LEN2R/W2LEN1R/W1LEN0R/W0reserviertG3L3G2L2G1L1G0L0
      0000       01
LEN = 00b bedeutet ja 1-Byte und
R/W = 00b bedeutet on execute

(Wenn evtl. schon Debug Register gesetzt worden sind, müsstest du erstmal über GetThreadContext die Inhalte der Register abfragen und dann gucken, ob eines von Dr0-Dr3 frei ist und beim Verändern von Dr7 müssen die alten Werte natürlich erhalten bleiben, also BPs löschen über and mit invertierten Werten und hinzufügen über or)

Soll jetzt noch ein On Write Breakpoint für eine Integer-Variable hinzugefügt werden, funktioniert das so:

Quote:
Context.Dr1 = &meine_variable;
Context.Dr7 |= 0x00D00004; // or Dr7, 00 00 00 00 11 01 00 00 00000000 0 0 0 0 0 1 0 0 b
LEN3R/W3LEN2R/W2LEN1R/W1LEN0R/W0reserviertG3L3G2L2G1L1G0L0
    1101       01  
LEN = 11b bedeutet 4-Byte und
R/W = 01b bedeutet on write

Um die Benutzung zu vereinfachen, schreibst du dir dann am besten eine HW BPs-Klasse o.Ä., wo du dann mit Bit-Shifts und Enums für Dr7 arbeitest statt mit konstanten Werten wie oben.

Zusätzlich geben dir B0-B3 aus Dr6 im Exception Handler an, ob die einzelnen Bedingungen der HW BPs erfüllt sind. Also wenn z.B. Dr0 gleich 401488h, R/W0 gleich 00b und LEN0 gleich 00b sind und EIP auch gleich 401488h ist, dann wäre diese Bedingung erfüllt und B0 wäre somit beim Eintritt in den Exception Handler gesetzt.
B0-B3 werden allerdings unabhängig von L0-L3 gesetzt, heißt um keine False-Positives zu kriegen, müsstest du zuerst L0-L3 abfragen, um zu wissen, welche HW BPs momentan aktiv sind, und dann kannst du das entsprechende Bx-Bit von Dr6 überprüfen, wodurch du dann in Erfahrung bringst, durch welche(n) HW BP(s) Single Stepping ausgelöst wurde.
Darüber hinaus kannst du natürlich auch einfach ExceptionAddress oder EIP abfragen und mit der Adresse, die du hooken möchtest, vergleichen und EIP dann neu setzen, also z.B. auf deinen Hook. Für das Aufrufen der originalen Funktion brauchst du aber logischerweise wie bei Detours ein Trampolin oder du musst den HW BP temporär deaktivieren, um eine Endlosrekursion zu vermeiden.

Anmerkungen:

* LEN gibt die maximale Größe an, heißt wenn LEN = 11b ist und dann über inc byte [meine_variable] auf die Variable zugegriffen wird, wird dennoch getrappt sprich: Single Step ausgelöst

* Bei On Execute-BPs LEN immer auf 00b setzen, wie Ende schon meinte

* Execute-Breakpoints trappen, wenn EIP auf der Adresse ist, heißt noch bevor die Instruktionen ausgeführt werden, was wiederum bedeutet, dass du den BP erst einmal wieder löschen musst, damit der Befehl ausgeführt werden kann, sonst wird an dem Befehl immer und immer wieder getrappt.
Data-Breakpoints trappen, nachdem auf die Speicheradresse zugegriffen wurde, also auf der nächsten Instruktion

* Global Level BPs gibt es im Protected Mode nicht, deswegen musst du immer L0-L3 setzen.

* Dr7.GE und .LE werden heutzutage nicht mehr verwendet, also sind diese beiden Bitfelder auch nicht von Interesse.

* Dr7.GD kann man anscheinend im Usermode nicht setzen und wird daher wahrscheinlich nur für den Kernelmode relevant sein.