OOP languages basically back you into this corner once a problem becomes complex enough. As an example, DI is supposed to decrease the coupling so that tests can actually test units and not wind up performing integration testing. Low coupling results in a more agile codebase, but does not intrinsically lead to more understandable code.
What goes wrong is people think that you need to "DI all the things." Like any tool it can be used incorrectly and, from what I have seen, violating the single responsibility principle [when using DI] is what most often leads to unwieldy code. "Got a model class/object? That needs an interface too."
Abstraction is not to blame, it is the incorrect usage of abstraction that is to blame; which is sadly what you are taught in school and college. Spending even one day learning a functional language is enough to drastically improve your understanding of how abstraction is supposed to work in OOP languages.
> I mean like even if you had the source code it's so convoluted you could never make any use of it.
Some IDEs can help with this (VS2015 just added "go to implementation" for C# interfaces), as does the most reliable way to understand code: stepping through it line-by-line (or at least method-by-method) in a debugger - once you have a run-time itable the callee is no longer ambiguous.
TLDR; it's a necessary evil for OOP languages but is often taken too far due to bad theory that is taught to us all.