Ich verstehe nicht wie sich der Offset ergibt



  • ich habe den Fake86-Emulator (http://sourceforge.net/projects/fake86/)
    ein bisschen erweitert um printf-Aufrufe mitzuloggen - leider kommen
    ich mit der LEA-Anweisung in diesem Kontext nicht wirklich klar

    Der ASM Code ist aus IDA-Pro und entspricht MASM/TASM-Syntax
    mein Ziel ist es den format_string und die va-args zu verarbeiten

    seg010:05CE ; int printf(const char *format_string, ...)
    seg010:05CE _printf         proc far                ; CODE XREF: sub_2EA2A+7P
    seg010:05CE                                         ; sub_2EA2A+16P
    seg010:05CE
    seg010:05CE var_8           = word ptr -8
    seg010:05CE var_4           = word ptr -4
    seg010:05CE format_string   = dword ptr  6
    seg010:05CE
    seg010:05CE                 push    bp
    seg010:05CF                 mov     bp, sp
    seg010:05D1                 sub     sp, 8
    seg010:05D4                 push    di
    seg010:05D5                 push    si
    seg010:05D6                 mov     si, 36D8h
    seg010:05D9                 lea     ax, [bp+format_string+2]
    seg010:05DC                 mov     [bp+var_4], ax
    seg010:05DF                 push    si
    

    ich dachte die LEA-Anweisung verstanden zu haben - aber ich habe einfach nicht
    die Daten gesehen die ich erwarte - dann ein bisschen rumgespielt und auf den Wert gekommen - in alle bisherigen Testfaellen

    kann mir jemand erklären wie ich auf die sp+4 komme?

    //...
    if(current_instruction_ptr == _printf_base)
    {
      uint16_t format_string_offset = *(uint16_t*)ptr(ss, sp+4);
      char* format_string = (char*)ptr(ds, format_string_offset);
    }
    


  • das ganze läuft unter DOS/16Bit

    und ich denke das mich dieser "typedef" verwirrt

    format_string = dword ptr 6
    


  • Memory Model: Medium - also sollte der format_string ein near-pointer sein

    erstellt ist das Programm laut IDA mit Quick C 2.x



  • Ich fang mal an zu raten, da ich nicht weiß was ptr(...) macht bzw sein soll. Zu DOS-Zeiten habe ich C gemieden wie der Teufel das Weihwasser :).

    IDA teilt dir den Aufrufer mit:

    ; CODE XREF: sub_2EA2A+7|P
    ; sub_2EA2A+16|P
    

    Das nennt sich "cross references" und du kannst in IDA einstellen, dass diese Referenzen dir so angezeigt werden, dass du die entsprechenden Code-Teile auch findest.

    Wenn Du dir den aufrufenden Code anschaust, kannst Du zweifelsfrei entscheiden, ob der CALL near oder far ist und ob der Zeiger auf format_string near oder far ist. Anhand des geposteten Codes vermute ich, dass CALL und Zeiger far sind.

    viele grüße
    ralph



  • ndisasm spuckt diesen code fuer die _printf aus - sieht gleich aus

    00000000  55                push bp
    00000001  8BEC              mov bp,sp
    00000003  83EC08            sub sp,byte +0x8
    00000006  57                push di
    00000007  56                push si
    00000008  BED836            mov si,0x36d8
    0000000B  8D4608            lea ax,[bp+0x8]
    0000000E  8946FC            mov [bp-0x4],ax
    00000011  56                push si
    


  • Ich fang mal an zu raten, da ich nicht weiß was ptr(...)

    liefert mir einfach einen echten Pointer (bei mir VS2010/Win32) auf den Emulator-Speicher (uint8_t RAM[1MB]...), die ss, sp usw. sind Register-Variablen in dem richtigen Zustand (also eben so wie alles beim _printf Aufruf steht Register, RAM, etc.)

    void* ptr(uint16_t seg, uint16_t ofs)
    {
      return &RAM[seg*16+ofs];
    }
    

    Wenn Du dir den aufrufenden Code anschaust, kannst Du zweifelsfrei entscheiden, ob der CALL near oder far ist und ob der Zeiger auf format_string near oder far ist. Anhand des geposteten Codes vermute ich, dass CALL und Zeiger far sind.

    es gibt nur 2 Aurufe von printf im Code

    B8 E0 3F        mov ax, offset seg012:1422 
    50              push ax
    9A CE 05 C5 2C  call _printf
    

    und 0x9A ist ein far call
    und der Zeiger auf den String wird als ax(16bit) gepusht also near

    also laut https://en.wikipedia.org/wiki/Intel_Memory_Model#Memory_models
    ist es dann Medium



  • Hintergrund:

    Fake86 ist ein kleiner PC-Emulator (in C) den ich mit(unter) VS2010 kompiliere/laufen lassen

    die _printf Routinen ist in einem Programm das in Fake86 unter DOS 6.22 gestartet wird - ich habe Fake86 so erweitert das ich den Start dieses Programmes erkenne und einen Image-Base-Pointer bekommen von diesem aus kann ich dann mit Offset-Informationen aus IDA den Funktionsaufrufe von _printf erkennen und z.B. die Parameter in einem Log-Fenster ausgeben



  • ich hole mir den Wert von SP direkt bei seg010:05CE (am Anfang der Routine) - da kommt dann aber noch ein push bp - und danach wird erst SP in BP geschoben - also schonmal -2 unterschied in SP

    damit wird kann ich mir das +2 schon mal erklären

    lea ax, [bp+format_string+2]
    

    aber wiso ist es dann +4 und nicht +6 bei mir im Code

    *(uint16_t*)ptr(ss, sp+4)
    

    es ist wohl schon zu spät



  • Gast3 schrieb:

    ich hole mir den Wert von SP direkt bei seg010:05CE (am Anfang der Routine) ...

    ... und damit hast du die Variable 'SP' und punkt. Was später kommt, interessiert erst mal nicht. Die Variable 'SP' bildet nur das Register SP zu dem Zeitpunkt ab, zu dem du dir dessen Wert holst. Später wird Register SP geändert, auf Register BP kopiert und mit Register BP weitergearbeitet ... interessiert alles die Variable SP nicht.

    Da der CALL mit Sicherheit far ist, liegt auf dem Stack eine Segment:Offset-Rückkehraddresse - also 4 Bytes. 4 Bytes später findest du den format_string, also SP+4.

    Ich bin allerdings immer noch der Meinung, dass der Zeiger auf den format_string far ist. Gibt es denn wirklich vor dem Aufruf von _printf kein weiteres 'push ds' oder 'mov [bp+xx],xx' oder ähnliches? Wie sieht denn die Stackbereinigung nach dem _printf aus? Ich bin jetzt aber zu faul QuickC zu installieren. Es ist schon spät :).

    viele grüße
    ralph



  • Gibt es denn wirklich vor dem Aufruf von _printf kein weiteres 'push ds' oder 'mov [bp+xx],xx' oder ähnliches?

    ich schaue morgen nochmal nach - aber denke nicht (auch wenn ich mein Quick C 2.51 Beispiel nicht kompiliert bekomme - aber wohl auch zu spät)



  • Ich hab mal mit Quick C Prof 2.51(2.52) eine kleine Medium-Model-Exe erzeugt

    #include <stdio.h>
    
    void main(void)
    {
      int brk = 0;
      printf("can load driver");
      printf("%s %i\n", "hallo", 10);
      brk = 1;
    }
    

    Exe mit "qcl /AM test.c" erzeugt (/AM = Medium Model)

    die _main sieht so aus

    ; int __cdecl main(int argc, const char **argv, const char **envp)
    _main       proc far        ; CODE XREF: start+B8P
    
    var_2       = word ptr -2
    
            push    bp
            mov bp, sp
            mov ax, 2
            call    __aFchkstk
            push    si
            push    di
            mov [bp+var_2], 0
            mov ax, offset format_string ; "can load driver"
            push    ax      ; format_string
            call    _printf
            add sp, 2
            mov ax, 10
            push    ax
            mov ax, offset aHallo ; "hallo"
            push    ax
            mov ax, offset aSI  ; "%s %i\n"
            push    ax      ; format_string
            call    _printf
            add sp, 6
            mov [bp+var_2], 1
            pop di
            pop si
            mov sp, bp
            pop bp
            retf
    _main       endp
    

    im Datensegment

    format_string	db 'can load driver',0  ; DATA XREF: _main+12o
    aHallo		db 'hallo',0            ; DATA XREF: _main+22o
    aSI		db '%s %i',0Ah,0        ; DATA XREF: _main+26o
    

    und die _printf dazu
    leider nicht exakt der gleiche Code (nur eine lokale Variable)
    denke es ist eher Quick C 2.01 oder sowas - aber der Aufruf ist ja gleich

    ; int printf(const char	*format_string,	...)
    _printf		proc far		; CODE XREF: _main+16P	_main+2AP
    
    var_4		= word ptr -4
    format_string	= dword	ptr  6
    
    		push	bp
    		mov	bp, sp
    		sub	sp, 4
    		push	di
    		push	si
    		mov	si, 0DEh ; 'Þ'
    		push	si
    		call	__stbuf
    		add	sp, 2
    		mov	di, ax
    		lea	ax, [bp+format_string+2]
    		push	ax
    		push	word ptr [bp+format_string]
    		mov	ax, 0DEh ; 'Þ'
    		push	ax
    		call	__output
    		add	sp, 6
    		mov	[bp+var_4], ax
    		mov	ax, 0DEh ; 'Þ'
    		push	ax		; FILE *
    		push	di		; int
    		call	__ftbuf
    		add	sp, 4
    		mov	ax, [bp+var_4]
    		pop	si
    		pop	di
    		mov	sp, bp
    		pop	bp
    		retf
    _printf		endp
    


  • 1. printf mit einem 16bit Wert

    call _printf
    add sp, 2 ; 1*2 byte vom stack aufraeumen
    

    2. printf mit 3 16bit Werten

    call _printf
    add sp, 6 ; 3*2 byte vom stack aufraeumen
    

    liegt IDA dann mit dem dword hier falsch? Das ist der Teil der mich verwirrt

    format_string = dword ptr 6
    


  • Ich bekomme mein IDA (6.1) einfach nicht dazu, mir dasselbe zu zeigen wie dir:

    seg001:0664 ; int printf(const char *, ...)
    seg001:0664 _printf         proc far                ; CODE XREF: _main+16|P
    seg001:0664                                         ; _main+2A|P
    seg001:0664
    seg001:0664 var_4           = word ptr -4
    seg001:0664 arg_0           = word ptr  6
    seg001:0664 arg_2           = byte ptr  8
    seg001:0664
    

    Build:

    C:\Compiler\C\QC25>qcl /AM test.c
    Microsoft (R) QuickC Compiler Version 2.51
    Copyright (c) Microsoft Corp 1987-1990. All rights reserved.
    
    test.c
    
    Microsoft (R) QuickC Linker  Version 4.10
    Copyright (C) Microsoft Corp 1989-1990.  All rights reserved.
    
    Object Modules [.OBJ]: /z2 test.obj
    Run File [test.exe]: "test.exe" /noi
    List File [NUL.MAP]: NUL
    Libraries [.LIB]:
    
    C:\Compiler\C\QC25>_
    

    Zumindest bin ich jetzt überzeugt davon, dass der Zeiger zum format_string tatsächlich near ist. Dein IDA liegt also falsch.

    viele grüße
    ralph



  • meine qlc Ausgabe ist 100% identisch - ich habe aber einen IDA 6.5.x

    was sagt ndisasm vom nasm (http://www.nasm.us/pub/nasm/releasebuilds/2.11.08/win32/nasm-2.11.08-win32.zip)

    einfach ndisasm -e XXX -b 16 test.exe > out.txt

    XXX = dezimaler fileoffset in IDA von der _printf routine (bei mir 2092)

    bei mir sieht die so aus

    00000000  55                push bp
    00000001  8BEC              mov bp,sp
    00000003  83EC04            sub sp,byte +0x4
    00000006  57                push di
    00000007  56                push si
    00000008  BEDE00            mov si,0xde
    0000000B  56                push si
    0000000C  E85701            call word 0x166
    0000000F  83C402            add sp,byte +0x2
    00000012  8BF8              mov di,ax
    00000014  8D4608            lea ax,[bp+0x8]
    00000017  50                push ax
    00000018  FF7606            push word [bp+0x6]
    0000001B  B8DE00            mov ax,0xde
    0000001E  50                push ax
    




  • Gast3 schrieb:

    http://msi.to/file/48132

    Deine TEST.EXE und meine TEST.EXE sind absolut identisch. Auch meine ndisasm-Ausgabe (ndisasm -v: NDISASM version 2.11.08 compiled on Feb 21 2015) deckt sich mit deiner Ausgabe.

    viele grüße
    ralph



  • mit jedem Update sieht alter DOS-Code anders aus - hab mich schon ein bisschen daran gewoehnt

    und danke nochmal fuer deine Hilfe
    jetzt klappt das Abgreifen der Parameter des 16bit _printfs
    meine eigene 32bit _detoured_printf16 spuckt das gleiche auf der Emulator-Log-Console aus


Anmelden zum Antworten