First, create a category for typical changes in terms of SLOC (and function points if it involves changes to UI elements.)
Then look at previous code changes and see where they fit in the category model described above.
Do the same for new features (that is, treat code changes and new features differently unless a new feature involves significant change/cost on the existing code base.) If integrating a new feature is trivial, separate it from code changes. If its integration with existing code base is significantly costly, lump it with the code changes.
Then, for each categorized code change (or new feature), determine 1) how long it took to complete, 2) how long was it supposed to take to complete, 3) how many people were actually involved in the completion, and 4) how many people were initially thought to be required for its completion.
So now you should have a tabular presentation of delivered artifacts (code changes and new features), each with columns indicating estimated and actual resources (hours, people) consumed.
You can then assume a ballpark average engineer salary/hour (say $40/hr). Multiply that by 2 to indicate total cost of engineer/hour (an estimation of hourly salary, electricity, office amenities and rental, etc.) It doesn't have to be accurate, just realistic enough.
For each delivered artifact A, you can compute the following:
estimated_cost(A) := avg_hr_salary * estimated_hours(A) * estimated_people(A)
approx_cost(A) := avg_hr_salary * (actual_hours(A) + estimated_hours(A))/2 * (actual_people(A) + estimated_people(A)/2
max_cost(A) := avg_hr_salary * actual_hours(A) * actual_people(A)
With these relations (which must be based on actual code changes or new items... otherwise they are meaningless), you can can calculate (per category size), what is the % of a code change of that size to deviate from the estimated size (a % of failure), the approximate cost as well as the % the code change might actually reach a max. cost.
Chances are the % for maximally deviating from the estimated (minimal) cost resembles more and more a exponential distributation the larger the code change is.
With that date, you can tell your customer the following:
You to your customer:
This change you are requesting (A)
might take 10K SLOC, on the old code
base. Historical data indicates that
it might take 2 people at a minimum
(estimate_people), possibly escalating
up to 3 people (actual_people). The
probability of the change to cost
(estimate_cost) is A%; B% for
approx_cost, and %C for max_cost.
Now, this is key. You have to do the same computations for new requests (the type whose integration with the old code base involves not-significant changes on the later).
If you find that the estimated, approx and max costs for new requests of a given size is (hopefully) significantly less than the costs of old codebase changes of the same size, THEN you have an argument for a code rewrite. You have given sufficient evidence that the old code base is expensive to change compared to changes of the same magnitude in new code.
But if the costs of new code requests do not greatly diverge from old code changes of the same magnitude, you will have a hard time justifying a code rewrite (and it might indicate that the problems are not only to the code base, but also in your development practices.)