В зависимости от операционной системы вы можете создавать динамические функции для каждого объекта обратного вызова. Я сделал именно это для обратных вызовов из унаследованного кода, которые не имели возможности передать значение обратному вызову.
Создайте небольшую функцию перенаправления прототипов с типом, ожидаемым библиотекой, который затем вызывает реальную функцию обратного вызова с легко видимым дополнительным параметром
void Call01020304 () {
CallWithValue(0x01020304);
}
Скомпилируйте его и посмотрите на гекс для сборки. Должно быть очевидно, где константа.
Затем вы используете VirtualAlloc + PAGE_EXECUTE_READWRITE или mmap + PROT_EXEC для выделения некоторой памяти, которую можно выполнить. Распределение обычно выполняется в блоках 4K, поэтому создайте класс для управления функциями, так как вы будете распределять достаточно для многих за один раз.
Когда вам нужна новая функция обратного вызова с уникальным значением, скопируйте байты прототипа с соответствующим измененным значением в вашу исполняемую память и верните указатель на нее как функцию обратного вызова.