2

I start by saying that I'm completely new to the topic of reversing although I have many years of experience with programming in general.

I've some problems with automatic recognition of library functions of a DOS executable compiled with Borland C++ 3.1.

Actually the signatures are correctly identified as bc31rtd (and it states 199 as the actual number of applied signatures). So for example strcmp is correctly identified, colored and such.

Starting from this I was relying blindly on these library function in the rest of the code until I realized that there was something wrong, this is, for example what I see in for strcpy:

enter image description here

Which doesn't make sense to me since src is not used at all. Then repne scasb should scan for the length of the string but the last value placed in di is [bp+dest+2] like if both const char* were not dd but dw (so just the offset, without any specified segment, and ds is used implicitly).

Since this was getting me crazy I checked the original implementation of the function by opening CC.LIB of BC++3.1 with IDA Pro directly and implementation is different indeed:

enter image description here

So where's the problem here?

How can I alter the function as I want? I tried modifying directly stack variables (Ctrl+K) but then offsets become faulty (eg [bp+8] marked as red).

I ask sorry if I'm making some trivial wrong assumption that I'm not realizing.

3

Oh, the happyness of near functions vs. far functions, near pointers vs. far pointers, and mixed models (far functions, near pointers)

Back in the 16 bit world, programs could either

  • use a maximum of 64 KB code, and 64 KB data, have all pointers use 16 bit, and ignore segment registers. This was called the near model, because all offsets were within the same segment

or

  • choose to be able to have more than 64 KB code, and more than 64 KB data; make pointers 32 bit (16 bit segment and 16 bit offset), and mess with segment registers whenever using a pointer. This was called the far model since pointers could point to different segments, in fact, the whole address space

or

  • use a mixed model - near pointers for code, and far pointers for data (more commonly used) or vice versa (i don't know of a single program that used that).

Now, the problem is, that the strcpy function from your program seems to be the far-code-near-data version, and the strcpy from the library seems to be the near-code-far-data version.

You see how the first function makes sure that es = ds (push ds; pop es) but doesn't mess with segments otherwise. This would make it use 16 bit pointers. The retf at the end suggests it uses the far code convention.

The second function uses LES di, [bp+src], which loads es and di at the same time. This means it uses far data pointers on the stack, the retn at the end means it uses the near code model.

What happened is that, in your disassembled program, IDA saw the retf, deducted far code, and probably assumed far data as well - which is wrong. If data was indeed far, you'd have 4 bytes for src, and another 4 bytes for dst, and this is what ida is showing on your stack. But in fact, the pointers have just 2 bytes each, so dest is at offset 6, but src is at offset 8, not 0x0a (10). Which is why accessing src is shown as dest+2, and the (wrong) offset 0x0a, which IDA falsely assumes to be src, isn't used at all.

Shown as a picture:

This is what IDA thinks:           and this is the real stack layout:

+----------------------------+     +--------------------------------+
|000c src  segment           |     |                                |
|000a src  offset            |     |                                |
|0008 dest segment           |     |0008 src                        |
|0006 dest offset            |     |0006 dest                       |
|0004 saved bp               |     |0004 saved bp                   |
|0002 return address segment |     |0002 return address segment     |
|0000 return address offset  |     |0000 return address offset      |
+----------------------------+     +--------------------------------+

To fix this, open the function prototype dialog, and change the pointer definitions to char near *.

  • Everythins is clear, thanks for taking the time to give me precise and complete answers because they're really useful in getting some expertise of this new world. So assuming this, the binary library object of Borland C++ compiler should contain multiple implementations of the same function and choose the one accordingly to the program model specified? I see I can tell IDA to use far code/data or near code/data in compiler options but other methods look like with far data so I wonder if in reality it's like mixed far/near data mode according to the function involved, is it plausible? – Jack Dec 9 '15 at 14:34
  • Well, you can't link in the near and far versions of strcpy at the same time (at least not without a lot of tricks involving changing the names of functions in standard libraries). But even if you compile for the near data model, you could explicitly define some big data arrays as far, while keeping the rest of your data near. You won't be able to use standard functions on these data arrays in that case, but this might be ok depending on your application. When speed is important, keeping as much as possible near helps, so there's a valid reason to do this. – Guntram Blohm supports Monica Dec 9 '15 at 14:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.