But ok, if ores are also in hills, i also haven't found some there yet. I usually don't dig complete hills down, but also haven't found ore when piercing tunnels through hills.
I guess the point is (and you can see this in biomes.xml) that the RNG chances for ore always start just a few blocks below topsoil (or topsand or topsnow, whatever) in every biome at every elevation. It doesn't even recognize "hill" or "mountain" as a thing, nor reference any sea-level elevation. It's just X layers below the surface, there's a chance for ore. It's still RNG (based on world seed) so of course there will be vast areas of nothing but stone.
For example here is one sub-biome of the Snow biome:
<layers>
<layer depth="1" blockname="terrSnow"/>
<layer depth="3" blockname="terrDirt"/>
<layer depth="*" blockname="terrStone">
<resource blockname="
terrOreIron" prob="0.6500" rwgGenerationType="all"/>
<resource blockname="
terrGravel" prob="0.7830" rwgGenerationType="all"/>
</layer>
<layer depth="3" blockname="terrBedrock"/>
</layers>
So this specifies one layer of snow (the "topsoil"), 3 layers of dirt, then all the rest of the layers (except the last 3 which are bedrock) are stone + a chance for iron/gravel.
Interestingly, the surface ore boulders are prefabs like a house might be.
I need to research that a bit more, but the implication is that every ore field attached to a surface boulder has the same layout underground (though it may be rotated).
<decoration type="prefab" name="
deco_iron_vein" checkresource="-7" onslopes="true" prob=".00657" rotatemax="3"/>
Edit: Hmm maybe not. I'm thinking that the "checkresource" parameter there actually forces the decoration/prefab engine to...uh...check for the resource underground (maybe 7 blocks down?) and then only place the "prefab" boulder over the top of a pre-existing ore field. The prefab itself does extend a few blocks down - basically the first 5-7 ore nodes are part of the prefab, which is just linking into whatever ore field the RWG put down there.