News:

Herr Otto Partz says you're all nothing but pipsqueaks!

Main Menu
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - llm

#46
my current solution for modifying games more or less safe is using dosbox as a backend
for example: im able to hook function calls and overwrite code parts, very good for porting because
you can port a function while the function is in use by the emulated code

for example the data compression routine of the Alpha Waves game
disassembled in IDA then converter to my tiny "emulator" that fakes the minimal
aspects of the x86 code to ease the porting to C

emu_t just got some registers, memory and methods that look like the original
asm and behave like the original asm code - but its just C/C++ code

this function gets called by dosbox when the emulated code actually wanted to call the original
16bit code, i can debug, step through it, log data, write unit-tests etc.

this is my third try to port that function properbly - before just in assembler and 16bit C
but subtile micro difference seemed to work but my port was until now only working with 95%
of the data

void UNCOMPRESS_sub_1BAE7(emu_t &e)
{
start:
e.push(e.es);
e.push(e.di);
e.cx = 0x80;
e.ax = e.ds;
e.es = e.ax;
e.di = 0x301;
e.xor(e.ax, e.ax);
e.rep_stosw();
e.pop(e.di);
e.pop(e.es);
e.sub(e.di, *e.word_ptr(e.cs, 0xBAA2));
e.ax = e.di;
e.shr(e.ax, 1);
e.shr(e.ax, 1);
e.shr(e.ax, 1);
e.shr(e.ax, 1);
e.cx = e.es;
e.add(e.cx, e.ax);
e.es = e.cx;
e.and (e.di, 0x0F);
e.add(e.di, *e.word_ptr(e.cs, 0xBAA2));
e.push(e.ds);
e.push(e.es);
e.push(e.si);
e.push(e.di);
e.cx = 4;
e.di = 0xBA9A; // offset byte_1BA9A; ???
e.ax = e.cs;   // seg seg000 // cs register; ???
e.es = e.ax;
e.lds(e.si, *e.dword_ptr(e.cs, 0xBAA4));
e.ax = e.si;
e.shr(e.ax, 1);
e.shr(e.ax, 1);
e.shr(e.ax, 1);
e.shr(e.ax, 1);
e.dx = e.ds;
e.add(e.ax, e.dx);
e.ds = e.ax;
e.and (e.si, 0x0F);
*e.word_ptr(e.cs, 0xBAA4) = e.si;
*e.word_ptr(e.cs, 0xBAA4 + 2) = e.ds;
e.add(*e.word_ptr(e.cs, 0xBAA4), e.cx);
e.rep_movsb();
e.pop(e.di);
e.pop(e.si);
e.pop(e.es);
e.pop(e.ds);
e.dx = *e.word_ptr(e.cs, 0xBA9C);
e.inc(e.dx);
e.cmp(*e.byte_ptr(e.cs, 0xBA9A), 0);
if (e.jnz())
goto loc_1BB63;
goto loc_1BC52;
// ---------------------------------------------------------------------------

loc_1BB63:
e.push(e.ds);
e.push(e.es);
e.push(e.di);
e.xor (e.ch, e.ch);
e.cl = *e.byte_ptr(e.cs, 0xBA9A);
e.di = 0x201;
e.ax = e.ds;
e.es = e.ax;
e.ds = *e.word_ptr(e.cs, 0xBAA4 + 2);
e.si = *e.word_ptr(e.cs, 0xBAA4);
e.add(*e.word_ptr(e.cs, 0xBAA4), e.cx);
e.rep_movsb();
e.cl = *e.byte_ptr(e.cs, 0xBA9A);
e.xor (e.ch, e.ch);
e.di = 1;
e.add(*e.word_ptr(e.cs, 0xBAA4), e.cx);
e.rep_movsb();
e.cl = *e.byte_ptr(e.cs, 0xBA9A);
e.di = 0x101;
e.add(*e.word_ptr(e.cs, 0xBAA4), e.cx);
e.rep_movsb();
e.pop(e.di);
e.pop(e.es);
e.pop(e.ds);
e.xor (e.ch, e.ch);
e.cl = *e.byte_ptr(e.cs, 0xBA9A);
e.xor (e.ah, e.ah);
e.bx = 1;
loc_1BBB4:
e.al = *e.byte_ptr(e.ds, e.bx + 0x200);
e.si = e.ax;
e.dl = *e.byte_ptr(e.ds, e.si + 0x301);
*e.byte_ptr(e.ds, e.bx + 0x402) = e.dl;
*e.byte_ptr(e.ds, e.si + 0x301) = e.bl;
e.inc(e.bx);
if (e.loop())
goto loc_1BBB4;
e.dx = *e.word_ptr(e.cs, 0xBA9C);
e.inc(e.dx);
e.cx = 1;
loc_1BBD2:
e.dec(e.dx);
if (e.jnz())
goto loc_1BBE1;
loc_1BBD5:
e.cmp(*e.byte_ptr(e.cs, 0xBA9B), 0);
if (e.jz())
goto locret_1BBE0;
goto start;
// ---------------------------------------------------------------------------

locret_1BBE0:
return;
// ---------------------------------------------------------------------------

loc_1BBE1:
e.push(e.ds);
e.si = *e.word_ptr(e.cs, 0xBAA4 + 2);
e.ds = e.si;
e.si = *e.word_ptr(e.cs, 0xBAA4);
e.lodsb();
*e.word_ptr(e.cs, 0xBAA4) = e.si;
e.pop(e.ds);
e.bx = e.ax;
e.cmp(*e.byte_ptr(e.ds, e.bx + 0x301), 0);
if (e.jnz())
goto loc_1BC01;
e.stosb();
goto loc_1BBD2;
// ---------------------------------------------------------------------------

loc_1BC01:
e.bl = *e.byte_ptr(e.ds, e.bx + 0x301);
e.xor (e.ax, e.ax);
e.push(e.ax);
goto loc_1BC35;
// ---------------------------------------------------------------------------

loop_x:
e.bp = e.ax;
e.cmp(*e.byte_ptr(e.ds, e.bp + 0x301), 0);
if (e.jz())
goto loc_1BC44;
e.cmp(e.bl, *e.byte_ptr(e.ds, e.bp + 0x301));
if (e.ja())
goto loc_1BC30;
e.al = e.bl;
e.bl = *e.byte_ptr(e.ds, e.bp + 0x301);
loc_1BC22:
e.bl = *e.byte_ptr(e.ds, e.bx + 0x402);
e.or (e.bl, e.bl);
if (e.jz())
goto loc_1BC42;
e.cmp(e.bl, e.al);
if (e.jb())
goto loc_1BC35;
goto loc_1BC22;
// ---------------------------------------------------------------------------

loc_1BC30:
e.bl = *e.byte_ptr(e.ds, e.bp + 0x301);

loc_1BC35:
e.al = *e.byte_ptr(e.ds, e.bx + 0x100);
e.ah = e.bl;
e.push(e.ax);
e.xor (e.ah, e.ah);
e.al = *e.byte_ptr(e.ds, e.bx);
goto loop_x;
// ---------------------------------------------------------------------------

loc_1BC42:
e.ax = e.bp;
loc_1BC44:
e.stosb();
e.pop(e.ax);
e.or (e.ax, e.ax);
if (e.jnz())
goto loc_1BC4C;
goto loc_1BBD2;
// ---------------------------------------------------------------------------

loc_1BC4C:
e.bl = e.ah;
e.xor (e.ah, e.ah);
goto loop_x;
// ---------------------------------------------------------------------------

loc_1BC52:
e.push(e.ds);
e.push(e.es);
e.cx = *e.word_ptr(e.cs, 0xBA9C);
e.push(e.cx);
e.ds = *e.word_ptr(e.cs, 0xBAA4 + 2);
e.si = *e.word_ptr(e.cs, 0xBAA4);
e.add(*e.word_ptr(e.cs, 0xBAA4), e.cx);
e.rep_movsb();
e.pop(e.cx);
e.pop(e.es);
e.pop(e.ds);
goto loc_1BBD5;
}
#47
Quote from: Cas on October 01, 2022, 04:45:58 PMYet, we had to do it that way because there was no other in this case, but a full rewrite of the game to C or a recreation with a new engine are the only options if we want a stable modded game

its still possible but you need to be very carefull - don't add code in between that moves code, always try
to be binary equal or not equal in very small well known parts

for example - adding only code by link-virus behavior, add a new segment, ignore relocation table changes, patch calls into the code (save the original code) - recover original code after running the new code - this way you can add large amounts of code without changing too much

or search the code for non-symbolic offsets and fix them to symbolic ones, then your able to change everything without problems - but that could be time consuming

but this is all in all very "tinker"


#48
Quote from: Daniel3D on September 30, 2022, 09:11:34 AM
Quote from: llm on September 30, 2022, 08:13:04 AM
Quote from: Daniel3D on September 23, 2022, 12:04:33 PMi had little time so i did an all or nothing approach.
Changing all files creates a near copy, but there are many bit differences, and it does not run,.

expected result :)
Yes and no.
After fixing a typo I did it again and although it still doesn't work, i did get a clear error message.
I forgot to write it down but it was something along the line of that it failed to read the contents of sdmain. Vsh..
So maybe..

Te correct version is already in the post above, just didn't go into details because I had no time for it at that moment.

you will get random problems when not beeing binary equal - and you will not be able to test all effects (its impossible) - to not create a binary compatible version (which is easy and clear what to do) is like asking for random trouble somewhere over the complete code - anytime in the future - the 100% binary equal version is by design correct

there is no "it seems to work" partially :) - every move of code is just wrong (what does not mean in any form that the game will crash - but still its wrong, for example subtile errors in the physic engine, speed, while drawing etc., unlimited amount of silly invisble bugs)

#49
Quote from: Daniel3D on September 23, 2022, 12:04:33 PMi had little time so i did an all or nothing approach.
Changing all files creates a near copy, but there are many bit differences, and it does not run,.

expected result :)
#50
Quote from: Cas on September 10, 2022, 04:08:18 AMOh, it's great that you've kept your C and assembly polished throughout the years.

im working in this business - would be sad if i would stop polishing it :)

Quote from: Cas on September 10, 2022, 04:08:18 AMAs you know, I've been wanting to start working more in C, but the lack of native graphics (native to the compiler, that is) has been stopping me.

ever tried Allegro? https://liballeg.org
Version 4.2 supports DOS (Allegro 4.4+ removed the support) https://liballeg.org/readme.html 

QuoteAllegro 4
Allegro 4 is the classic library, whose API is backwards compatible all the way back to Allegro 2.0 for DOS/DJGPP (1996). It is no longer actively developed, but we still apply patches sent to us by contributors, mainly to fix minor bugs. Every so often we will make new releases.

Allegro 4.4 supports the following platforms:

Unix/Linux
Windows (MSVC, MinGW, Cygwin)
MacOS X
Haiku/BeOS
PSP (currently in git repository only)
The older Allegro 4.2 branch additionally supports:

Windows (Borland)
QNX
DOS (DJGPP, Watcom)

i don't know if its worth to invest time in this area to come up with your own solution
just wrap the stuff in you graphs.h/c and replace it later when everything is working - the best
pixel drawing routine is for nothing if its not called for doing something magical :)
#51
did it work to change the segment alignment to para?
#52
Quote from: Cas on September 06, 2022, 08:44:39 PMIt is possible to have zero-length code... in a way. This is what many viruses do. You analyse the original code, then make it shorter by either using more space-efficient code or dropping things that are not being used. Finally, use the newly available space to introduce new code.

im well aware of that and using it activly in some of my reversing projects
im in the assembler,C/C++, reversing, disassembling area more or less full time per day for the last 15 years - there is nearly nothing i haven't touched in that time :)
#53
Quote from: Daniel3D on September 06, 2022, 11:37:42 AMCas and I used asmorig in all cases. Also checked the executable against game.exe (the execombined executable)

im mean the unchanged asmorig - just to prevent other problems with your extensions
(it a clean test that do not need to happen in a already changed source)

Quote from: Daniel3D on September 06, 2022, 11:37:42 AMAl changes in the code are local and besides a offset with the last modification there are no differences besides what is to be expected as far as I can tell.

your changes must move code and offsets - because new code can't be of size 0 (or did Cas magic?)

Quote from: Daniel3D on September 06, 2022, 11:37:42 AMWe didn't touch the version with c code substitution because that brings extra uncertainty to the mix.
We kept it as close to unmodified original as possible.
Now we know more we could try the same thing with c code. See what effect that has and if that works better or not.

its very possible to only use c code for your extensions - adding a new segment for example and some patching, the other c ports are not needed to use c code for your stuff
#54
QuoteDo they need to be changed in the ASM file and the INC file simultaneously for each segment?

yes all segment names/definitions need to be equal

AND you should base on the original asm - i have no idea if you and Cas checked if the exe changed with your extension in an unwanted way (what does not mean in any way that it would crash, could be that it just works but still wrong)
#55
Quote from: Daniel3D on September 05, 2022, 01:56:38 PMBut that's relative easy to do i guess .

more or less - one need to change (starting with the very last segment, going up) to change "byte" to "para" - assemble and check if anything in the exe changed, then the next segment, one by one - producing as many exes as segments available

seg041.asm
dseg.asm
seg039.asm
seg038.asm
...
seg000.asm

from last to first because then its easier to see in a hex-editor what parts changes down, if you start with seg000 everything will change because the segments are orderd seg000, ... ,seg039, dseg, seg041 (segment 40 is the dseg (data segment))

it will mostly result in some bytes more (or less) around segment ends/begins then in the previous exe - which needs to be removed(added) in the assembler source

#56
Quote from: Cas on September 04, 2022, 08:45:26 PMwe can't tell if something starts at a location because the previous block was padded with extra bytes or because it just ended there, so we just align to the byte

one need to check if removing these bytes + segment align=para works - it should, but that isn't just replacing byte with para - every segment needs to be checked in the resulting exe to prove that it works - because you said it need to be aligned and para was were common in that days

#57
Quote from: Daniel3D on September 04, 2022, 12:11:55 PMIf this works on unmodified code...

that is what i wrote :)

Quotebut the hard introduced bytes before/after the segments needs to be adjust to get the very same binary again
#58
so we talk about segment alignment (i think that is your case here)
and sometimes about offsets that are partially (or fully) non-symbolic

seg011 segment byte public 'STUNTSC' use16
the "byte" in the segment definition means no alignment so the assembler
will not put in alignment bytes before - but the segment needs to start
at a paragraph (or divideable by 16) address that far code/data dependencies can work

https://docs.microsoft.com/en-us/cpp/assembler/masm/segment?view=msvc-170

the reason for having "byte" as segment alignment here is due to disassembling
the disassembler can't detect if the code works or not - or if the code uses some magic trick to make it work at runtime so it falls back to 1:1 reversing aka "keep the byte offsets intact" - that means there are sometimes alignment bytes around the segment that the disassembler didn't detect as segment alignment (there are so many posibilities so the disassembler just uses the always working one)

seg011 segment PARA public 'STUNTSC' use16
would force the assembler to always align the segment to a paragraph adress - but the hard introduced bytes before/after the segments needs to be adjust to get the very same binary again

that would be maybe a first step

the partially or full inner non-symbolic-offset problems are different

#59
Quote from: Cas on September 02, 2022, 10:30:28 PMAlignment issues not necessarily result from purposeful alignment by the compiler.

alignment means the direct positioning of data/code to fullfill "others" needs: hardware(bus) or for example dos API needs - the normal positioning of data and code in an executable is usually not called "alignment" (inside of struct there is also "padding")

alignment is nearly not needed in 16bit code on x86 (but very needed for SPARC for example)

dword, words are not aligned (as in a normal 32bit windows/linux program) in stunts - usually the old compilers just ignored padding in any form
and most handwritten assembler code also ignored any form of alignment or padding - everything is packed together with no space between

so we normaly talk about offsets the change

#60
the code-segment count is defined by the Microsoft compiler/linker
seg010 is for example std library code most others are game code, the splitting is mostly up to the compiler or how the libs were designed at start, there seems (not prooved) parts that are fully assembler based (maybe the engine, but could be also that only some functions, not segments are pure assembler based)

QuoteThings I would like to do would be of the sort of easily inserting things knowing that they won't break the code

alignment isn't a real problem here - just changing the offsets of code is

hard to tell as long there is no deep analysis of non-symbolic offsets in the code
evil stuff like addressing a variable by using another variables-symbols plus a offset etc.

the very first routine that gets run when the exe starts is (this is the first code that gets jumped after DOS loaded the exe and done the relocation)

seg010:0012 start          proc near
this is the pre main routine that setups the stdlib stuff etc and calls the user main function

this is the standard int main(argc,argv,envp)
seg000:0000 ; int __cdecl stuntsmain(int p_argc, const char **p_argv, const char *envp)
and code like this for example is problematic

seg010:007C                 mov     di, 55CAh       ; offset in dseg where uninitialized data starts
seg010:007F                 mov     cx, 0AD20h      ; original size/end of dseg
seg010:0082                 sub     cx, di

the real problem is: you can not easily test if your changes are correct by just playing the game and looking for bugs - its so super easy to
introduce bugs without noticing it but the second or third extension - weeks/month later will trigger it hard enough etc. - working on
pure disassembled code is much much harder then working on real handwritten assembler :(