But for NIZK this doesn't appear to be the case and the proof of zk fully breaks down if you have to choose a hash function
Why you say so?
The original HVZKP's Simulator is a good starting point for Fiat-Shamir NIZK's Simulator since the hash output is uniform enough. The added requirement is that RO must be programmable by the Simulator (maintaining the uniformity), to take into account the fact that simulators often play with stuff out of order.
ROs (and hashes) commit their output to the input, so they aren't programmable, they enforce a temporal order of messages; but remember that Simulators don't play with the rule of the protocol, they can produce fake transcripts acting out of protocol rules, and Random Oracle/Hash programmability is one of their "superpowers"
So, IMHO, passing from theoretic RO to actual Hash the only additional assumption for ZK is just enough uniformity of hash output.
EDIT TO ANSWER OP's FOLLOW-UP COMMENT
Fiat-Shamir prescribes the Random Oracle used by the prover and by the verifier is the same.
Simulator is an entity playing out of protocol rules, but verifier has to follow them. So in Fiat-Shamir when the simulator is progamming the random oracle, it programs it for both the prover and verifier. Imagining the verifier using "original" oracle instead of the simulator-programmed-one puts him out of protocol, so it's out of our boundaries.
Using Hashes to simulate RO means prover and verifier have their own implementations of the prescribed hash, but it doesn't mean they can use different ones (if they want to follow the protocol); the simulator has to programme both the prover's and verifier's hash implementations.
Of course this sounds very strange, but not so much if you take into account that sometimes rewinding capability of simulator in interactive proofs is represented as prover and verifier being VMs in a computer and the simulator being the VMs hypervisor, able to pause and repeat their runs manipulating their state: in that case simulator can pause and rewind time flow... a quite huge superpower I would say :)
Coming back to our verifier calling an hash... now the prover and the verifier are VM which never interact (a part from the prover leaving the proof somewhere for the verifier to read it), but they both call their functions implementing the hash. And now our "hypervisor" simulator can change how both those functions works... why this should be worst than pausing and rewinding time?
Your central point seems to be the fact a verifier using "original" hash (the one not programmed by the simulator) would recognize the simulator's fake transcript:
- I have just said in that case the verifier wouldn't be following protocol rules cause it would use a RO/hash different from the one of entity pretending to be the prover;
- more, that detection capability imho is fundamental in any non-interactives proofs (Fiat-Shamir based or not): otherwise NI-ZKPs would be deniable, making themselves useless (a NI prover could always pretend that a previously published proof is fake and produced by a simulator)