Spoken like a true programmer. :)
Assuming your increase the size of the binary, change the first instruction to jump to the end of the program, do your magic, perform the original first instruction, and finally jump to the second instruction. If the program itself does not self modify or otherwise examine its own size, this would get you to the original start with your modifications applied.
> In the end, it took about 8 hours to get this project finished, which was spread out over multiple weeks.
That’s incredible.
If I were in need of a mod like this, I'd just wire another switch in parallel to control the appropriate solenoid.
His expensive electronic device now functions, from a UI perspective, like a normal tap.
Thank goodness traditional taps just work, and were invented before all this "smart" technology made our stuff so annoying.
Other than that, you’d be right :-)
OP didn't do this, because as they said: "I don’t like writing assembly". Although what they did is arguably harder...which maybe was the point! After all they could've just hooked up an MCU to proxy the button press for a given duration, which would've taken a lot less time.
The usual approach for a non-trivial patch is exactly what the OP did here: put your patch somewhere in free space, and replace the original sequence of instructions with a jump to your patch.
DnSpy lets you do this (that's why I was careful to prefix decompilers with "native"in my answer). You can edit snippets of decompiled C# code directly inside. But vm based runtimes are much easier to decompile/recompile, and it still may not work in the end.
Though programs compiled to intermediate code are in general very different to analyze, so that's kind of nitpicking.
With C and C++ things are a lot harder.
This is why traditional binary patching is the usual go-to option, because it doesn't require much upfront effort to perform. The downside is that you're constrained by the memory map of the original program and contorting your modifications to fit can be a huge pain.
There are alternative, less widely known techniques out there with different tradeoffs. For example, if you can make the original program bytes relocatable then you can run those through a linker and let it mend both original and new parts together. I've built tooling to help do that specifically (delinking programs back to object files [1]), it's faster than a decompilation project because you merely need to figure out and undo all the relocation spots, but you don't get source code at the end.
* not designed to be compiled again. It most likely won't compile without serious manual fixing. For example, decompilers often insert "pseudo-functions" to denote that something not easily representable in C is happening. Like CONCAT(var1, var2) may mean that both var1 and var2 are used as a single variable obtained by concatenating their bits (in practice: AX is used when AH and AL were already determined to be variables). Similar intrinsics exist for carry bits, jumps to arbitrary pointers, etc. This sometimes means that type inference went wrong somewhere, which brings us to...
* not perfect, and not designed to be perfect. Decompiler has no idea if a variable on stack is a qword or 8 byte array, it can only guess from local usage. In many cases array will be incorrectly decompiled as a single variable, or pointer parameter as an integer. This is not a huge problem when reversing, but catastrophic for decompilation. Automated struct identification is even harder, often almost impossible when you take unions into account. As a reverse engineer you are supposed to fix that interactively during analysis.
* decompilers in general don't decompile global data definitions - you interpret memory using a separate view. And for a good reason - what may look to the decompiler like three consecutive independent variables may actually be an (unrecognized) structure that must be kept in the same exact layout. Defining them as three independent C variables would almost surely not work.
* for firmware in particular the binary may be required to follow a specific layout or have other unusual characteristics (like volatile memory regions). No decompiler will give you a correct linker script to use for recompilation.
These problems are just out of the top of my mind. Worth noting that a new and upcoming reversing tool called rev.ng actually has working recompilation from C as one of their planned features, so we'll see what they come up with (and if they succeed).
The true hack would be to purchase a mechanical water filter, probably much higher quality for the money spent, and have it attached to the plumbing. Bonus is it works when the power's out.
Aren't these water dispensers built into most fridges nowadays?
Well, no. That's a solution, and it's probably practical, but in no way is buying a replacement a "hack".
Not to say I would put one of these machines in my house, but I can see how it would be superior in some ways.