Disclaimer: since the question is very general, so is this answer.
Let’s consider the example of the CyanogenMod (now retired…) Theme Engine. Here are some excerpets from an article that described how it worked (emphasis added here):
At the most basic level, themes are simply a mechanism to allow resources to be replaced at runtime … just think of them as the elements which make up the application’s UI. Whenever an application is started, Android loads up the resources associated with that app and the application makes requests for these resources.
That’s the normal flow of retrieving resources, but what happens when a theme is applied? When the system loads the resources for the application, it checks if there is a theme applied and if there is, it adds the themed resources to the original resources. This is the point at which the magic happens. When the app requests a resource, the system will check if there is a themed version of it and if so, returns the themed resource, and if not it simply returns the original. The key point here is the original resources are never modified/moved/altered, the system simply returns a resource from the theme instead of the original. As you can see in the diagram below, there is no difference as far as the application is concerned.
This is done by leveraging a framework known as Runtime Resource Overlays, which was contributed to AOSP by Sony in 2014. Part of the RRO framework is a tool called IDMAP. IDMAP inspects the resources in an application and compares the resource types and names to those in the themed resources. For those resources that match it stores a mapping of the original resource to the matching resource in the theme. Once this process is complete we are left with an efficient way to determine if a resource is themed and where that resource resides.
… and it goes on some more.
A more recent tool that does this is called Substratum1, 2.
I think these should get you on the right path.
solved Manipulating the UI of existing native applications at runtime