Autor Beitrag
scrooge
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 200



BeitragVerfasst: Sa 24.07.04 19:21 
Hi,

hab seit längerer Zeit ein Prog namens "inmemexe" afu meinem Computer liegen.
Das Programm sartet sich sebst neu und ändert dann seinen Code in den einer Resource (Resource ist eine andere Exe (soweit ich mir das denken kann), die eine Proc innerhalb einer Dll aufruft).

Hab mir jetzt mal endlich nach langer Zeit mal Pe-Format angeguckt und kapiers soweit.
Gibt nur noch eine Codezeile die ich nicht ganz raffe:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
Context.ContextFlags := CONTEXT_INTEGER; // Context : TContext
      if GetThreadContext(ProcessInfo.hThread, Context) and  
         ReadProcessMemory(ProcessInfo.hProcess, Pointer(Context.Ebx + 8), // was kann man an der Stelle ebx + 8 auslesen. Soweit ich weiß ist das ja die Stack-Basis-Adresse. Der Prozess auf den sich das bezieht ist übrigens der selbe wie das laufende Prog (d.h das selbe Image) und wurde per CREATE_SUSPENDED erstellt.
          @BaseAddress, SizeOf(BaseAddress), BytesRead)


Schon mal danke für die Mühe
obbschtkuche
Gast
Erhaltene Danke: 1



BeitragVerfasst: Sa 24.07.04 19:29 
EBX ist ein "general purpose register". Vermutlich liegt darin eine Variable oder ein Parameter... :?

Stackpointer ist ESP (Extended[?] Stack Pointer)
raziel
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2453

Arch Linux
JS (WebStorm), C#, C++/CLI, C++ (VS2013)
BeitragVerfasst: Sa 24.07.04 19:39 
EBX wird im allgemeinen Base Register genannt, hat aber mit dem Stack recht wenig zu tun. Was Du meinst ist EBP, der zum Sichern des Stackframe beim Eintritt in Funktionen dient, wenn Parameter und/oder lokale Variablen benötigt werden.
Es gibt als General Purpose Register noch EAX (Accumulator Register), ECX (Counter Register) und EDX (Data-Register). Man kann sie aber i.a. füllen mit was man will, deshalb wäre mehr Code interessant und vor allem wäre interessant, wozu das Ganze gut sein soll!

raziel

_________________
JSXGraph
FAlter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 192

Win XP Home
Turbo Delphi 2006 (Win32), C# (VS 2008 Prof.), Java (Eclipse), PHP
BeitragVerfasst: Sa 24.07.04 21:04 
Soweit, so gut. Um jetzt noch die richtige Reihenfolge zu nennen: EAX, ECX, EDX, EBX

Das E weist auf 32-Bit hin (könnte wirklick Extended heißen oder Expanded). Bei 16 Bit einfach AX etc. verwenden. Hier mal 'ne Grafik für die Zusammenhänge:

ausblenden Quelltext
1:
2:
3:
4:
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
+------------------------------------------------------------------------------------------EAX+
                                              +---------------------------------------------AX+
                                              +---------------------AH+ +-------------------AL+


Bits 0 - 31 sind das EAX-Register. Die Bits 0 - 15 sind gleichzeitig auch AX, welches wiederum in AL (0 - 7) und AH (8 - 15) unterteilt ist.

Genauso auch bei den anderen auch.

EBX lässt sich zum adressieren verwenden. Wobei man SEGMENT:BASIS addressiert und EBX stellt eben diese Basis dar (meistens DS:EBX). Darum EBX.

ESP (oder SP bei 16-Bit, gibt kein SH/SL) addressiert genauso, jedoch addressiert es das unterste Element des Stacks (SS:ESP). Bei PUSH wird ESP um 4 verringert und der Wert an die neue Position geschrieben.
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: So 25.07.04 04:41 
FAlter hat folgendes geschrieben:
Soweit, so gut. Um jetzt noch die richtige Reihenfolge zu nennen: EAX, ECX, EDX, EBX

lol ... srry, aber das ist so falsch...
Richtiger wäre:
EAX EDX ECX (EBX)*

Laut meiner Dokumentation zum Thema Assembler (gibt's unter webster.cs.ucr.edu/index.html zum DL):
Meine verwendete ASM Referenze zum Thema Parameter-Übergabe hat folgendes geschrieben:
Parameters are transferred to procedures and functions via CPU registers or the stack, depending on the routine’s calling convention. For information about calling conventions, see Calling conventions.
Variable (var) parameters are always passed by reference, as 32-bit pointers that point to the actual storage location.
Value and constant (const) parameters are passed by value or by reference, depending on the type and size of the parameter:

An ordinal parameter is passed as an 8-bit, 16-bit, 32-bit, or 64-bit value, using the same format as a variable of the corresponding type.
A real parameter is always passed on the stack. A Single parameter occupies 4 bytes, and a Double, Comp, or Currency parameter occupies 8 bytes. A Real48 occupies 8 bytes, with the Real48 value stored in the lower 6 bytes. An Extended occupies 12 bytes, with the Extended value stored in the lower 10 bytes.
A short-string parameter is passed as a 32-bit pointer to a short string.
A long-string or dynamic-array parameter is passed as a 32-bit pointer to the dynamic memory block allocated for the long string. The value nil is passed for an empty long string.
A pointer, class, class-reference, or procedure-pointer parameter is passed as a 32-bit pointer.
A method pointer is passed on the stack as two 32-bit pointers. The instance pointer is pushed before the method pointer so that the method pointer occupies the lowest address.
Under the register and pascal conventions, a Variant parameter is passed as a 32-bit pointer to a Variant value.
Sets, records, and static arrays of 1, 2, or 4 bytes are passed as 8-bit, 16-bit, and 32-bit values. Larger sets, records, and static arrays are passed as 32-bit pointers to the value. An exception to this rule is that records are always passed directly on the stack under the cdecl, stdcall, and safecall conventions; the size of a record passed this way is rounded upward to the nearest double-word boundary.
An open-array parameter is passed as two 32-bit values. The first value is a pointer to the array data, and the second value is one less than the number of elements in the array.

When two parameters are passed on the stack, each parameter occupies a multiple of 4 bytes (a whole number of double words). For an 8-bit or 16-bit parameter, even though the parameter occupies only a byte or a word, it is passed as a double word. The contents of the unused parts of the double word are undefined.
Under the pascal, cdecl, stdcall and safecall conventions, all parameters are passed on the stack. Under the pascal convention, parameters are pushed in the order of their declaration (left-to-right), so that the first parameter ends up at the highest address and the last parameter ends up at the lowest address. Under the cdecl, stdcall, and safecall conventions, parameters are pushed in reverse order of declaration (right-to-left), so that the first parameter ends up at the lowest address and the last parameter ends up at the highest address.

Under the register convention, up to three parameters are passed in CPU registers, and the rest (if any) are passed on the stack. The parameters are passed in order of declaration (as with the pascal convention), and the first three parameters that qualify are passed in the EAX, EDX, and ECX registers, in that order. Real and method-pointer types do not qualify as register parameters, but all other parameters do. If more than three parameters qualify as register parameters, the first three are passed in EAX, EDX, and ECX, and the remaining parameters are pushed onto the stack in order of declaration. For example, given the declaration

procedure Test(A: Integer; var B: Char; C: Double; const D: string; E: Pointer);

a call to Test passes A in EAX as a 32-bit integer, B in EDX as a pointer to a Char, and D in ECX as a pointer to a long-string memory block; C and E are pushed onto the stack as two double-words and a 32-bit pointer, in that order.

Register saving conventions

Procedures and functions must preserve the EBX, ESI, EDI, and EBP registers, but can modify the EAX, EDX, and ECX registers. When implementing a constructor or destructor in assembler, be sure to preserve the DL register. Procedures and functions are invoked with the assumption that the CPU’s direction flag is cleared (corresponding to a CLD instruction) and must return with the direction flag cleared.


FAlter hat folgendes geschrieben:
EBX lässt sich zum adressieren verwenden. Wobei man SEGMENT:BASIS addressiert und EBX stellt eben diese Basis dar (meistens DS:EBX). Darum EBX.

Bei 32-Bit-Assembler geht das aber auch mit jedem anderem Register, wenn man im aktuellen Segment bleibt:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
asm
    mov EDX, EAX
    xor EAX, EAX
    mov [EDX-$00000004], EAX
end;

(Der Code cleart (IIRC) die Funktionsadresse des Destructors des Objects Self. Gibt beim Freigeben dann ne Exception (AV um genau zu sein).

FAlter hat folgendes geschrieben:
ESP (oder SP bei 16-Bit, gibt kein SH/SL) addressiert genauso, jedoch addressiert es das unterste Element des Stacks (SS:ESP). Bei PUSH wird ESP um 4 verringert und der Wert an die neue Position geschrieben.

Yip. POP funktioniert genau andersrum:
Wert einlesen und dann (E)SP inkrementieren:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
asm
    mov reg, [ESP]
    inc ESP, 4
end;

_________________
Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
scrooge Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 200



BeitragVerfasst: So 25.07.04 16:41 
Danke für die ausführliche Berichtigung ;-)

Hier mal der ganze Code:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
program InMemExe;

uses
  Windows,
  ntddk in 'ntddk.pas';

{$R res\Image.res}

function Protect(Characteristics: ULONG): ULONG;
const
  Mapping: array [0..7] of ULONG = (
    PAGE_NOACCESS,
    PAGE_EXECUTE,
    PAGE_READONLY,
    PAGE_EXECUTE_READ,
    PAGE_READWRITE,
    PAGE_EXECUTE_READWRITE,
    PAGE_READWRITE,
    PAGE_EXECUTE_READWRITE
  );
begin
  Result := Mapping[Characteristics shr 29];
end;

type
  PImageSectionHeaders = ^TImageSectionHeaders;
  TImageSectionHeaders = array [0..95] of TImageSectionHeader;
var
  ProcessInfo: TProcessInformation;
  StartupInfo: TStartupInfo;
  Success: Boolean;
  Context: TContext;
  BaseAddress: Pointer;
  BytesRead: DWORD;
  Resource: Pointer;
  NtHeaders: PImageNtHeaders;
  BytesWritten: DWORD;
  Sections: PImageSectionHeaders;
  i: ULONG;
  OldProtect: ULONG;
begin
  FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  StartupInfo.cb := SizeOf(TStartupInfo);
  if CreateProcess(nil, PChar(ParamStr(0)), nil, nil, False, CREATE_SUSPENDED,
    nil, nil, StartupInfo, ProcessInfo) then
  begin
    Success := False;
    try
      Context.ContextFlags := CONTEXT_INTEGER;
      if GetThreadContext(ProcessInfo.hThread, Context) and
         ReadProcessMemory(ProcessInfo.hProcess, Pointer(Context.Ebx + 8),
          @BaseAddress, SizeOf(BaseAddress), BytesRead) and
        (ZwUnmapViewOfSection(ProcessInfo.hProcess, BaseAddress) >= 0) then
      begin
        Resource := LockResource(
          LoadResource(0, FindResource(0, 'Image', 'EXE')));
        if Assigned(Resource) then
        begin
          NtHeaders := PImageNtHeaders(
            Cardinal(Resource) + Cardinal(PImageDosHeader(Resource)._lfanew));
          BaseAddress := ntddk.VirtualAllocEx(ProcessInfo.hProcess,
            Pointer(NtHeaders.OptionalHeader.ImageBase),
            NtHeaders.OptionalHeader.SizeOfImage,
            MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
          if Assigned(BaseAddress) and WriteProcessMemory(ProcessInfo.hProcess,
            BaseAddress, Resource, NtHeaders.OptionalHeader.SizeOfHeaders,
            BytesWritten) then
          begin
            Sections := PImageSectionHeaders(ImageFirstSection(NtHeaders));
            for i := 0 to NtHeaders.FileHeader.NumberOfSections - 1 do
              if WriteProcessMemory(ProcessInfo.hProcess,
                Pointer(Cardinal(BaseAddress) + Sections[i].VirtualAddress),
                Pointer(Cardinal(Resource) + Sections[i].PointerToRawData),
                Sections[i].SizeOfRawData, BytesWritten) then
                ntddk.VirtualProtectEx(ProcessInfo.hProcess,
                  Pointer(Cardinal(BaseAddress) + Sections[i].VirtualAddress),
                  Sections[i].Misc.VirtualSize,
                  Protect(Sections[i].Characteristics), OldProtect);
            if WriteProcessMemory(ProcessInfo.hProcess,
              Pointer(Context.Ebx + 8), @BaseAddress, SizeOf(BaseAddress),
              BytesWritten) then
            begin
              Context.Eax := ULONG(BaseAddress) +
                NtHeaders.OptionalHeader.AddressOfEntryPoint;
              Success := SetThreadContext(ProcessInfo.hThread, Context);
            end;
          end;
        end;
      end;
    finally
      if not Success then
        TerminateProcess(ProcessInfo.hProcess, 0)
      else
        ResumeThread(ProcessInfo.hThread);
      CloseHandle(ProcessInfo.hProcess);
      CloseHandle(ProcessInfo.hThread);
    end;
  end;
end.


ntddk enthält die nativen Api's.

// PS: E bedeutet Extended