Catch processBlock() on decompiled assembly of my plugin

Hi there,

I’m trying to decompile my own plugin (using Ghidra right now) made with JUCE.
For some reasons: learning, testing a reverse engineering path, and so on.

As starting point, I’d like to intercept the processBlock() block code, but for any try I did, I’m not really able to do that.

Can you point me out some tips on doing that?
Did you ever experienced somethings similar?

Thanks

So, this isn’t really about JUCE, etc, but about learning to reverse engineer code? I ask because you can certainly look at the assembly code from with the debugger.

Can I look at the assembly code “optimized” while debugging? Because it will be very different…

How will it be different? The assembly code that the debugger shows you, is the assembly being generated by the compiler. Yes, you can get different assembly based on your compilation optimization flags - but the debugger will show you that code too. Debuggers don’t alter the code - they show you as honest a view of what is being executed on your CPU as is possible.

As for decompiling and inspecting your code with Ghidra, what you will need to do is learn how the compiler optimization semantics effects the code that you’ll see in front of you, first of all. There are optimization flags where processBlock() will be optimized away (maybe) and there are flags where you will have huge binaries and all your functions will be where you expect them to be, too.

If you are just starting out with reverse engineering, don’t bother with Ghidra. Instead, learn what the -S (emit assembly) flag does with your compiler, and read the generated assembly code - FIRST. Then, change your optimization flags, do another full rebuild, and re-read the assembly code again. You will gain much more knowledge this way, starting out, than by diving into Ghidra head-first without understanding these things.

Use your compiler. Know your compiler, always. It is the front line for all of your engineering and reverse-engineering efforts.

Some interesting details about the -S flag and how to use it here:

And if you’re on clang:

1 Like

Another tool that I highly recommend if you want to get started to learn how C++ code translates to assembly using different compilers and compiler optimisation flags is the compiler explorer: godbolt.org. This video might be helpful as well getting started as it shows both the usage of compiler explorer and some good basic overview of how to read assembly code Just Enough Assembly for Compiler Explorer

2 Likes

Each compiler has a method of inserting a breakpoint into the code programmatically, this will break when run under a debugger.

Windows: __debugbreak()
macOS: __builtin_debugtrap()

2 Likes

I see. But once the code is emitted as optimized flags, is there a way to catch the starting point (i.e. processBlock()) by putting a … comment for example? Else, how can i start to catch the process function?

One way to find where processBlock starts is to search for the assembly instruction that changes the mxcsr register, which is used by ScopedNoDenormals (assuming you’re using that at the very start of processBlock and that you’re on Intel). Instructions to search for are ldmxcsr and stmxcsr.

3 Likes

‘break’ instructions are not removed in optimised builds.

No, comments are not part of your generated code. The way to do it is with your normal debugger (non-Ghidra) - merely put a breakpoint on processBlock(), run until you hit the breakpoint, then use your IDE to show you the assembly of the code at the current breakpoint. This will give you enough info to actually ‘reverse engineer’ what your code is doing in assembly but it will also show you the address of the processBlock method in memory - and later on when you are ready, you can then use that memory address in Ghidra to jump straight to that block of disassembled code …

1 Like

I’m with VSCode, using this tutorial to create a plugin: https://partialaudio.com/blog/juce-on-windows/

So no Projucer. Is it easy to setup debugger? Or better start using Projucer?

Projucer doesn’t have anything to do with debugging - its a front-end for generating JUCE-based projects that can be easily ported to different platforms - the .jucer file is all you need to generate the projects on each OS you wish to support with your final product.

VSCode includes the ability to debug - and if you are not familiar with it yet you should start there. You can do a lot of reverse engineering with VSCode and its debugger, alone - and until you’ve done this you really won’t have the insight you need to continue further with more advanced reverse-engineering tools such as Ghidra.

Really, it seems like you need to bootstrap your own local debugging capabilities, and this is definitely worthwhile to do with VSCode.

Didn’t use it :slight_smile: The fact is: I’m recovering some old plugin which I miss the code eheh
Any other call that I can recognize easily?

I did it in the past with gcc and VCV Rack. I’m just looking for a fast way to intercept it, without too much effort…

I’m sure at 100% on my old DLL i was using:

juce::dsp::IIR::Coefficients<float>::makePeakFilter

and this:

output = Clamp(-1.0f, 1.0f, output);

Without install the whole debugger things, can you kindly write to me to which instructions I could search on Ghidra? Such as the ones suggested by @kerfuffle about ScopedNoDenormals (which I didn’t use).

Many thanks please :slight_smile:

Not sure which Clamp function you use, but as suggested above, try out compiler explorer. Here you can see e.g. the assembly generated for std::clamp. Note that this might still greatly vary depending on the compiler (version) used an the context where the function is called, the compiler might decide to compute it differently in case the surrounding code looks different since it could re-use computations done somewhere else and a lot more.

Did you strip symbols from the DLL when you built it? If not, then Ghidra can probably tell you where you used makePeakFilter.

The native of JUCE:

image

And since its a .vst3, I believe its x64 msvc v19.37…

Anyway, I know I’ve used tan, but if I search it on Ghidra, can’t “catch” it:

This is a custom clamp function. Free functions from the JUCE library all start with a lowercase letter by convention and furthermore the equivalent function from JUCE is named jlimit. Last but not least what you are showing there is not a function but a function template with a super simple implementation – this will most likely be completely inlined by the compiler anyway.

If things like that aren’t clear to you, you will probably have no success finding out anything meaningful with a tool like Ghidra.

So what are you trying to achieve here in general? In case you will finally find the processing code, what do you want to do with it? Are you planing to re-use the decompiled code and re-use it in a new project or what is your plan?

Exactly, without loss the already covered work. Its all clear what you are saying, know a bit how compiler deal with code, but can’t match a basic skippet to start with on process() function, damn…