Also, zuerst sollte man sich für ein System entscheiden.
Dann die Kenntnisse dazu aneignen.
Möchte man sich mit Windows beschäftigen, sind Kenntnisse der
WinAPI (die von Windows für Programme angebotene Schnittstelle ) nützlich, da die meisten "direkt" kompilierten Sprachen (also kein .NET/Java) diese Schnittstelle nutzen, um z.B ein Fenster darzustellen, Benutzereingaben einzulesen, Dateien zu speichern usw.
Der restliche Code des Programms kümmert sich dann mehr oder weniger um die Verarbeitung dieser Daten.
D.h: wenn man weiß, dass per GetDlgItemText eine Benutzereingabe eingelesen wird, kann man nach dem Aufruf dieser Funktion den Code analysieren. Durch WinAPI Kenntnisse lässt z.B vermeiden, dass man fälschlicherweise die "BunteFensterZeichnen_10000_Zeilen_code" Routine analysiert, obwohl diese total unwichtig ist.
Höhere Sprachen und deren Frameworks verbergen zuviel vor dem Nutzer - das ist zwar sehr Vorteilhaft, wenn man nicht gerade hardwarenah Programmieren muss - allerdings bekommt man nichts von den Abläufen mit[1]. Natürlich sollte man sich auch mit "abstrakteren" Sprachen beschäftigen (mit denen auch die meisten Anwendungen programmiert werden

) - aber auch gleichzeitig damit, wie sie letzendlich "low-level" umgesetzt werden. Da die ganzen "abstraken" Sachen aber letzendlich aus simpleren Anweisungen zusammengesetzt werden, finde ich es zumindest besser, wenn man die "simpleren" prozeduralen Sprachen kennt und sich dann mit OOP Sprachen/Framworks beschäftigt, da diese darauf aufbauen (zumindest was die letzendliche Umsetzung in ausführbaren Code angeht). Kenntnisse der Sprache, in der die Anwendung geschrieben wurde, sind beim Reversengineering nie verkehrt, da man oft auf gleichnahmige Bibliotheken, Aufrufe oder Zeichenketten-fragmente stößt, die einem die grobe Orientierung erleichtern. Da man aber irgendwo einsteigen muss - wäre zumindest für Windows meine Empfehlung: WinAPI, Asm, C und dann ein paar abstraktere Sprachen kennenlernen.
[1]Da gibt es genug "lustige" Beispiele - angefangen mit dem Rat (einer "Schutzanwendung gegen Softwarepiraten"): der Programmierer möge doch kritische String nicht im Klartext schreiben, sondern als Chr(52)+Chr(95)+Chr(98 ) zusammensetzen

bis hin zu "geheimen" Methodenaufrufen eines Objects, die zwar aus der OOP Sicht perfekt sind (Methode wird nur aufgerufen, wenn aus Benutzernamen+Regkey mittels XYZ kompliziertem Verfahren ein String herauskommt, der exakt diesen Methodennamen hat - also Object.CallByName(Ergebnis) ), dabei aber leider nicht berücksichtigt wird, dass das ganze intern mittels String und Adresstabellen umgesetzt wird und man den Namen einfach ablesen kann.