1. Reading code
2. Bug classes
Step one, reading code:
There is a lot of crash courses on c/c++ on youtube.
I suggest binge watching a couple of them first.
Step two, Bug classes:
Bug classes :https://www.youtube.com/watch?v=lbjS2mXyMEQ&list=PLZYVq5u0Eud1jj-8k4vMiAg2PVTUC7ojb&index=3&ab_channel=Nobody
Watch these videos on c/c++ bug classes.
These videos are based of the book "secure coding in c/c++", which I can also recommend.
Assembly crash course: https://www.youtube.com/playlist?list=PL038BE01D3BAEFDB0
I would recommend watching the intro to assembly. Just so you know what you're looking at when you see assembly instructions in the debugger. Assembly is very easy and most programs only use a handful of instructions (mostly just moving things from memory to registers, doing operations on them or conditionals and moving them back to memory)
Step three, Debugging:
You can find videos on youtube on how to use windbg.
The main things you need to figure out is:
1. placing breakpoints and break on access (bp and ba)
2. viewing memory (i.e dd <address>)
3. freezing threads to hunt for races (~* and ~<num> f, ~<num> u)
4. Dv command for local vars, and dt for displaying memory as a specific type
5. Callstack (i.e kc)
additionally, learn how to enable page heap in global flags or if in kernel mode, special pools in driver verifier. These are essential when hunting for bugs. You can figure out what these are used for using google.. using google is probably more important then reading code. Get good at using google.
Combining all three to become super 0day bug hunter leet hacker:
It is very important to become very skilled at reading code first.
Do not get into closed source before you become adept in reading code.
For some bug classes, you can get away with only superficial knowledge of reading code.
But for bug classes, such as object lifetime related bugs, you need to have a good understanding -not just 'theoretical understanding'- on how objects are used in real world code. You can only get that by reading a lot of code and looking at a debugger.
There are plenty of open source projects where you can start practicing 'reading code'. Plenty of open source server applications or open source programs where file format parsing happens.
Once you have deep knowledge of c/c++, taking on a closed source program will be so much easier.
The thing that seperates a good reverse engineer from a mediocre one, is the ability to get a picture of what the original code may have looked like, decompilers, which generate pseudo code will help, but if you are not deeply familiar with the original programming language a program was written in, staring at assembly all day is just like shooting yourself in the foot. You may find bugs, but you will find much more by first dedicating time learning to read code to the point it becomes your second language.
The way I would practice reading code is as I described here: https://sandboxescaper.blogspot.com/2021/10/the-polar-bear-method.html
1. Find an open source project
2. Find an entry point for attacker controlled input
3. Try to get a breakpoint for that entry point to hit in windbg
4. Modify attacker controlled input to hit different code paths
This is a very engaging way of learning code. You are combining all 3 'pillars' from above, reading code, debugging and knowledge of bug classes.
The main thing you need to become aware of, is that, simply the act of reading code, learning more things, is progress. Finding bugs is not an indicator of progress.. and in the first few years it should not be your end goal. Just learn to enjoy reading code and learning more about it.
Finding a bug, especially when starting on a new component, it can take weeks.
Just become proficient at reading code and debugging, you can worry about finding bugs later.
In the end, some code simply does not have the required complexity or large enough code base for there the actually be any bugs. An important skill is to know when to stop an audit of a component and move to another component with more complexity.
I would also recommend strongly against fuzzing unless you first get proficient at these three things. You can find bugs fuzzing without having a deep understanding of your target, for sure.. but again, it is like shooting yourself in your foot and slowing down your progress. To find bugs with fuzzing in hard targets, you need to also have a deep understanding of the component you're fuzzing. And this can only be done by being proficient at reading code and debugging.
If you do not have code, use a program like ida, ghidra or binary ninja to explore a codebase, find an entrypoint for attacker controlled input or interesting code you want to trigger in your debugger. But again, you should only do this after already being proficient at reading c/c++.