jak podmienić kod programu w locie bez własnego runtime

3

załóżmy że mam programa odpalanego "natywnie"?

generuje sobie executable przy użyciu gcc

#include <stdio.h>

int main(int argc, char ** argv)
{
    printf("Argument %i = %s\n", 1, argv[1] );
    return 0;
}

gcc programik.c

[email protected]:~/test# ./a.out 50
Argument 1 = 50

czy w tym momencie moim runtime jest OS?

jeżeli tak, to czy da się bez tworzenia własnego "vma" "runtime" zmieniać kod w locie takiego programu? tak jak JIT?

jak to robią normalne języki?

1

Program to tez tylko sekwencja bajtow, wiec jesli moglbys wziac i w RAMie podmienic wartosci to wykonaloby sie to co bys tam sobie wpisal.
Kwestia jest jeszcze taka, ze masz wirtualna przestrzen adresowa wiec tak na prawde jadro musialoby dac Ci dostep do tej pamieci.

EDIT: To co pisalem wyzej dotyczylo podmiany kodu innego procesu. Proces ma dostep do swojego kodu ale domyslnie jest chyba read-only (??)

3

Można stworzyć obszar pamięci, który jest jednocześnie zapisywalny i wykonywalny. Jednak np. macOS/AArch64 tego zabrania, przez co trzeba zmienić JITy:
https://openjdk.java.net/jeps/391

macOS/AArch64 forbids memory segments from being executable and writeable at the same time, a policy known as write-xor-execute (W^X). The HotSpot VM routinely creates and modifies executable code, so this JEP will implement W^X support in HotSpot for macOS/AArch64.

https://en.wikipedia.org/wiki/W%5EX

0
WeiXiao napisał(a):

jeżeli tak, to czy da się bez tworzenia własnego "vma" "runtime" zmieniać kod w locie takiego programu? tak jak JIT?

Tak, w końcu JIT dokładnie to robi. Możesz nadpisać istniejący kod, możesz zaalokować nowy obszar pamięci i tam wygenerować kod, a potem go wykonać. Ograniczają Cię jedynie uprawnienia od systemu operacyjnego i architektury komputera.

0

@Wibowit:

Można stworzyć obszar pamięci, który jest jednocześnie zapisywalny i wykonywalny.

no dobra, a masz jakiegoś hello worlda gdzie wrzucam sobie np. Kod A, a za 5min podmieniam funkcje w kodzie A na inną implementacje?

za cholerę nie mogę nigdzie znaleźć jak takie coś ma wyglądać

@Afish

da się to z poziomu C# w miarę sensownie zrobić (tzn. nie pisać C w C# i wszystko w unsafe scopie)?

Tak, w końcu JIT dokładnie to robi

Zawsze wydawało mi się że to dzięki CLR/JVM jest ta podmiana w locie

8
WeiXiao napisał(a):

da się to z poziomu C# w miarę sensownie zrobić (tzn. nie pisać C w C# i wszystko w unsafe scopie)?

Sensownie jak sensownie, musisz generować kod maszynowy. Nie trzeba do tego unsafe'a, ale to jest niebezpieczne z definicji, więc to tylko żonglowanie API. Zerknij na

- w ostatnim demie dokładnie to robię, podmieniam kawałek kodu maszynowego, jakbyś chciał, to mógłbyś sobie napisać framework do robienia tego. Tak samo w https://blog.adamfurmanek.pl/[...]unch-of-bytes-in-c-revisited/ po prostu generuję tablicę bajtów (która jest siłą rzeczy gdzieś w pamięci) a potem wykonuję te bajty jako kod maszynowy.

WeiXiao napisał(a):

Zawsze wydawało mi się że to dzięki CLR/JVM jest ta podmiana w locie

No ale CLR/JVM to nie jest jakaś tam magia, to tylko kawałek kodu maszynowego. Masz kupę bajtów, która generuje kolejną kupę bajtów w locie i następnie skacze do tych bajtów aby je wykonać.

0

C++ nie ruszałem od studiów, ale tam chyba można było mieć wskaźnik do funkcji. Więc pewnie tam będą metadane i kod maszynowy.

1

W asemblerze można sobie ustawić uprawnienia dla konkretnego segmentu binarki: https://flatassembler.net/docs.php?article=manual#2.4

section '.text' code readable executable

Dopisujemy writeable i mamy komplet :) Z drugiej strony, nie jestem pewien czy coś takiego przejdzie.

0

@Afish:

FunctionInt function = FuncGenerator.Generate<FunctionInt>
(
    new byte[]{
        // Accessing variables on the stack directly instead via base pointer since we are too lazy to generate method frame
        0x8B, 0x44, 0x24, 0x04,         // mov eax, DWORD PTR [esp + 0x4]
        0x83, 0xC0, 0x04,   // add eax, 4
        0xc3                // retn
    }
);

noooooooooooooo, to jest porządny kawał 🍖🍖🍖🍖

czyli zatem jakbym sobie wziął jakiś intermediate representation, wykorzystał np. llvmowy toolchain do wygenerowania native codu i wrzucił go w byte[] i odpalił na innym wątku, to brzmiałoby to jak plan?

1
WeiXiao napisał(a):

czyli zatem jakbym sobie wziął jakiś intermediate representation, wykorzystał np. llvmowy toolchain do wygenerowania native codu i wrzucił go w byte[] i odpalił na innym wątku, to brzmiałoby to jak plan?

Koncepcyjnie tak. Technicznie — to zależy, co chcesz osiągnąć. Jeżeli chcesz to wołać bezpiecznie i używać obiektów z dotneta, to musisz uwzględnić konwencje wywołania, zwalnianie obiektów przez GC, spójność sterty i takie tam. Ale koncepcyjnie jak najbardziej o to chodzi.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0