Consider the following expression:

    expr = Hold[
       {
        f[
         {1, {Print@1}}
         ],
        g[
         {{{Print@1}}}
         ]
        }
       ];

I'm looking for a way to apply a replacement rule to the contents of all expressions with head `f`. To give a more concrete example, let's say I want to replace `Print` by `Echo`, but only inside `f`.

**Restrictions**: I have no knowledge about the exact structure around and inside `f` - there could be more or less nesting going on. This means the replacement rule can't capture the wrapper with `Hold` attribute, nor can it capture the individual parts of `f[…]`. (And the evaluation of `Print` should be prevented of course)