One way to do this is to create "shim" DLLs with similar names so that they are easy to recognize. This technique can be used incrementally (i.e. additional functions can be added to the DLL at any time, for other programs with similar requirements, rather than using an entirely new DLL each time).
The initial problem is that you can't simply replace the function names (i.e. CriticalSectionEx -> CriticalSection), because of the additional parameters that are required.
The shim technique requires that you change only the name of the imported DLL in the exe file, for example from "kernel32.dll" to "kernel31.dll" (to preserve the length of the name and maintain data alignment). Then you create a "kernel31.dll", which is placed in the same directory as the exe file. The contents of this "kernel31.dll" file will be an export table, an import table, and wrappers for some functions.
To create the import table, you need another program which will read the import table of your original exe, and perform a GetProcAddress on each entry. For each entry that returns a valid address, the "kernel31.dll" export table will have that entry added directly, and will point to the "kernel31.dll" import table in order to forward that imported name to the original DLL. This function has no compatibility issues.
For each entry that returns a zero for the address, the "kernel31.dll" export table will point to a wrapper inside "kernel31.dll" that you would have to write yourself.
Repeat this procedure for each of the DLLs in the original exe (so "ntdll.dll" could become "ntdl1.dll", "user32.dll" could become "user31.dll", etc).
There are two special cases, though: LoadLibrary and its variants (and its equivalent in ntdll.dll), and GetModuleHandle and its variants (and its equivalent in ntdll.dll). These require wrappers in the "kernel31.dll" because they might attempt to access dynamically DLLs or functions which will have compatibility issues, too. Unfortunately, you can't really determine that through static analysis.
These two wrappers will call the original function in kernel32.dll (or ntdll.dll), and then check the result. If a DLL does not exist, then it's a case of creating that DLL yourself, in case it was introduced in a later version of Windows, or returning this result if the DLL is truly optional. If the function does not exist, then it's a case of finding the reference to the DLL and renaming it, and then writing the function wrapper for a new version of that DLL.
That's a horribly long answer, and probably makes it sound more difficult to implement that it really is, but the details are needed.