Arch Linux Planet

Die propietären Grafikkartentreiber der Nvidia-340xx-Serie (und allem, was damit zusammenhängt) wurden aus den Repos entfernt, da eine vernünftige Betreuung durch die Arch-Entwickler nicht mehr gewährleistet werden kann (der letzte Rechner im Besitz segnet gerade das Zeitliche).

Benutzer einer entsprechenden Hardware wird empfohlen, auf die freien Treiber (nouveau-Paket) zu wechseln.

https://wiki.archlinux.org/index.php/No … _installed

Die PKGBUILDs wurden übrigens nicht ins AUR verschoben. Wer sie dennoch braucht, muss sie sich mittels "asp"
besorgen. Das Hochladen ins AUR ist nicht verboten.

Wie im englischen Forum beschrieben, startet systemd-timesyncd nach einem Update auf systemd-242.0-1 ggf. nicht mehr.
Es muss dann ein manueller Eingriff erfolgen:

# rm -rf /var/lib/systemd/timesync /var/lib/private/systemd/timesync

Es wurde ein neues monatliches Installationsimage veröffentlicht. Dieses trägt die Bezeichnung 2019.04.01. Anders als bei den letzten Images wird ab sofort der Kernel in Version 5.0 verwendet, was für eine höhere Hardwarekompatibilität sorgen kann. Der Download ist wie immer auf der Downloadseite in verschiedenen Formaten verfügbar.

Der Download ist für neue Arch-Nutzer interessant, oder für alle, die gern ein aktuelles Installations- bzw. Live-Image von Arch haben möchten. Für bestehende Installationen ist der Download wie immer nicht nötig.

Am Ziel angekommen

28.03.2019

Hallo liebe Community,

vielleicht kurz etwas zu meiner Geschichte. Den ersten Kontakt mit Arch Linux hatte ich vor zwei Jahren, leider habe ich es geschafft das System so zu zerschießen, dass es mir nicht mehr möglich war es irgendwie wieder zu fixen. Nach einer Zeit des Distrohopings kann ich nun stolz verkünden, das Arch Linux schon seit einigen Monaten auf meinen Produktivsystemen läuft. Und ganz ehrlich ich möchte es nicht mehr missen. Kein System lief bei mir bis jetzt so problemlos und dabei mit so aktuellen Paketen. Noch dazu habe ich bei Arch Linux so viele Freiheiten wie fast nirgends (abgesehen von Gentoo und LfS). Dafür auch nochmal von mir ein großen Dank an die Arch Linux Community. Werde versuchen mich mit einzubringen und somit etwas zurückzugeben.

Auch neu hier ...

27.03.2019

... und bin seit einer Woche mit Arch Linux auf einem meiner "älteren" Schlepptops mit Arch Linux unterwegs (T61) - und was soll ich sagen:
Insgesamt bin ich angenehm überrrascht!

Zu mir
Bj 61, Geek. Bastel gerne am Gerät wink

Nach längerer Linux_Abstinenz (letzte Versuche waren mit LM Sonya, oder Sylvia wink -  und vorherigen Ubuntu-, Debian- Katastrophen und meinen bescheidenen Suse ~5.3 Kernel-Bau-Experimenten gg. Ende der 90iger, bin ich
über den ct'uplink Podcast 25.1 vom 01.12.2018 auf ArchLinux gestolpert.
Hätte nicht gedacht, dass ich mich jemals wieder an eine "hand-made" Linux_Distro heranwage, es mich sogar wieder interessiert und mit den üblichen Stolpersteinen doch relativ geschmeidig zurecht komme wink

Aktuell sind ein paar Probleme aufgetaucht und an das Advanced Dock wage ich mich noch nicht heran. Aber mit solch einer netten, hilfsbereiten und versierten Arch Community kann ja nix mehr schiefgehen wink

Nun denn Willkommen und auf ein neues Linux-Abenteuer smile
feba

---------------------------------------------------------------------------------------------------------------------------------------
T61, Intel 965PM model 8898, cpu T5750, IGMA X3100, 4GB, SSD256, , TP Advanced Dock

Das richtige Partitionierungsprogramm

Seit der jüngsten Aktualisierung erstellt das grub-Paket nicht mehr automatisch eine Konfigurationsdatei in /boot/grub. Auch das Verzeichnis selbst fällt durch diese Änderung weg.


Siehe auch hier.

Dank an drcux, dem dies aufgefallen war.

Sehr cool !
Der neue lts-kernel 4.19.xx zeigt mir jetzt 0 Fehler nach dem Booten auf meinem System.
(journalctl -xb -p err)

grep.asm – Schritt 2: Ein besseres cat

Voriges Posting dieser Serie.

Sorry für die lange Wartezeit. Geht nicht wirklich schnell voran im Moment. Tja.

Auch dieses Posting braucht wieder einen Disclaimer: Da ich kein Assembler-Experte bin, bin ich über jede Korrektur und jeden Verbesserungshinweis dankbar!

Mehr Daten auf einmal lesen

Eines der großen Probleme der vorherigen Version war, dass wir ein Byte nach dem anderen gelesen haben. Dadurch wurde der Code zwar ein bisschen einfacher, aber das ist wirklich langsam. Wenn man erwartet, mehr als nur ein paar Bytes zu lesen, dann sollte man das anders machen. Mein bevelbar liest von STDIN und liest in der Tat Byte für Byte, aber da fließen eben auch nicht viele Daten. Die meiste Zeit über schläft bevelbar, also interessiert es keinen.

Anders bei einem grep. Mein voriger Code hat etwa 1.8 Sekunden gebraucht, um eine Datei von 4 MB zu verarbeiten. Das ist echt nicht gut. Jetzt dauert es nur noch etwa 60 Millisekunden, was immer noch nicht schnell ist, aber schonmal deutlich besser als vorher.

Dieser Performance-Sprung liegt daran, dass jetzt eben nicht mehr Byte für Byte gelesen wird. Man erinnere sich, dass „Byte für Byte lesen“ bedeutet, für jedes Byte einen Syscall absetzen zu müssen. Jeder Syscall ist mit viel Overhead verbunden und daher kommt es zu einer unnötigen Verzögerung. Um das zu vermeiden, allokiert das Programm jetzt einen großen Puffer von 4 MB und setzt nur noch einen read()-Syscall ab. (Idealerweise. read muss nicht alle angeforderten Bytes tatsächlich lesen, sondern kann auch schon vorher zurückkehren. Dann muss man es nochmal aufrufen, um den Rest zu bekommen.)

Faul bin ich aber immer noch: Das Programm liest den gesamten Input, bis ein End-of-File erreicht wird. Das heißt, wenn man durch eine Datei von 1 GB Größe greppen will, dann braucht man 1 GB an Speicher. Das lässt sich später aber leicht optimieren – ich habe das Programm schon so organisiert, dass sich die anderen Funktionen nicht mehr um dieses Implementierungsdetail kümmern müssen. Die arbeiten alle mit einem „struct“, in welchem beschrieben ist: „Hier ist ein Puffer, die aktuelle Zeile fängt hier an und ist so lang.“

Okay, so sieht die Hauptschleife jetzt (ohne Kommentare) aus:

.text
.global _start

_start:
    pushq %rbp
    movq %rsp, %rbp

    subq $64, %rsp
    movq %rbp, %rdi
    call init_buffer_info

    .Lreadwriteloop:
        movq %rbp, %rdi
        call next_line

        cmpq $0, %rax
        je .Lreadwriteloop_end

        movq %rbp, %rdi
        call test_match

        cmpq $0, %rax
        je .Lreadwriteloop

        movq %rbp, %rdi
        call print_current

        jmp .Lreadwriteloop

    .Lreadwriteloop_end:
        movq $0, %rdi
        call exit

In Pseudocode:

- Initialize a thing called "buffer info".
- Loop:
    - Read a line. (Update "buffer info" and mark the current line.)
    - Exit if no line left.
    - Test if the current line matches a pattern.
    - Loop again, if it doesn't.
    - Print the current line.

Im Moment wird beim ersten Aufruf von next_line() alles in einen großen Puffer eingelesen. Spätere Aufrufe aktualisieren dann nur noch Pointer und Counter. Wie man aber sehen kann, ist das für die Hauptschleife und die Funktionen egal.

Wieso? Auf dem lokalen Stack von _start wird Speicher für 64 Bytes allokiert. Dort drin speichern wir dann ein „struct“ namens „buffer info“. „struct“ meint hier im Wesentlichen dasselbe wie ein struct in C. Es enthält diverse Felder:

 -8(%rbp), 8 = pointer to data (char *)
-16(%rbp), 8 = allocated size of entire buffer (size_t)
-24(%rbp), 8 = current fill of buffer (size_t)
-32(%rbp), 8 = offset for current line in buffer (size_t)
-40(%rbp), 8 = length of current line (size_t)

So ziemlich alle Funktionen arbeiten mit diesem struct: next_line() sorgt dafür, dass der Pointer und die Counter auf die aktuelle Zeile zeigen; test_match() arbeitet nur auf der aktuellen Zeile; print_current() gibt ausschließlich die aktuelle Zeile aus.

Die Details von next_line() überspringen wir jetzt. Da sind viele Kommentare drin. Ähnlich wie bei der vorigen Version verwaltet es Speicher mit dem Syscall brk() und liest Daten mit read().

Mit rep stosb Speicher initialisieren

Eine frühe Version von init_buffer_info() sah so aus:

movq $0, -8(%rdi)
movq $0, -16(%rdi)
movq $0, -24(%rdi)
movq $0, -32(%rdi)
movq $0, -40(%rdi)

Heißt also, es wird nur ein Haufen Nullen in den Speicher geschrieben. Das sind jetzt nur fünf Instruktionen, die nur ein Mal während der gesamten Laufzeit des Programms aufgerufen werden, also spielt’s sehr wahrscheinlich keine Rolle, wie man das macht. Die Frage stellte sich aber trotzdem: Was, wenn man ein paar Megabytes an Speicher initialisieren wollte? Oder mehr?

Klar, man kann es so machen (für len >= 1):

movq $0, %rcx
movq $pointer, %rdi
.Lloop_start:
    movb $0, (%rdi)
    inc %rdi
    inc %rcx
    cmpq $len, %rcx
    jne .Lloop_start

Aber gibt’s da keinen besseren Weg? Da ich nun nicht allzu viel Erfahrung mit Assembler habe, ist das beste, was ich finden konnte, der „rep“-Mechanismus, der mit „string instructions“ zusammenarbeitet.

In diesem konkreten Fall benutzen wir mal rep stosb:

movq $pointer, %rdi
movq $40, %rcx
movb $0, %al
rep stosb

Wir schreiben also eine 40 in %rcx, dann ein NULL-Byte nach %al. stosb liest dann dieses Byte aus %al aus und schreibt es im Speicher an die Stelle von %rdi. (Segmentierung ignorieren wir.) stosb erhöht dann auch automatisch den Pointer %rdi um eins, damit beim nächsten Aufruf das folgende Byte im String angefasst wird. Und wie ruft man stosb nochmal auf? rep erledigt das. rep lässt die Instruktion immer und immer wieder laufen, bis irgendwann %rcx den Wert 0 erreicht.

Das ist also eine sehr knappe Form von memset().

Noch ein kleines Detail. String-Operationen wie stosb können den Wert in %rdi erhöhen, wie oben gesehen, oder verringern. Das hängt davon ab, ob das „direction flag“ gesetzt ist. Mit std kann man es setzen oder mit cld löschen.

Noch mehr rep: Mit repne scasb ein strlen() implementieren

Okay, unser grep liest also einen großen Haufen Daten und schreibt sie in einen Puffer. Den muss es dann natürlich durchsuchen, um die einzelnen Zeilen herauszufischen – weil grep ein zeilenorientiertes Programm ist.

Man erinnere sich an das struct vom Anfang:

 -8(%rbp), 8 = pointer to data (char *)
...
-32(%rbp), 8 = offset for current line in buffer (size_t)
-40(%rbp), 8 = length of current line (size_t)

Bei jedem Aufruf muss next_line() also offset erhöhen und length aktualisieren. Das tut es, indem es bei pointer + offset startet und dann jedes folgende Byte prüft, ob es ein Newline-Character ist. Sobald einer gefunden ist, wird die beobachtete Länge in length abgelegt.

Das ist im Wesentlichen dasselbe wie das traditionelle strlen(), bloß dass eben auf ein Newline geprüft wird und nicht auf ein NULL-Byte.

Nun könnte man das natürlich so programmieren, dass man einfach manuell eine Schleife schreibt. Genau das hatte ich zuerst auch gemacht und das sah so aus:

current_line_length:
    /* Find the length of the current line.
     *
     * in:
     *   %rdi, pointer to base of info struct
     *
     * out:
     *   %rax, current line length */

    movq $0, %rax
    movq -8(%rdi), %rbx
    movq -24(%rdi), %rcx
    movq -32(%rdi), %rdx
    addq %rdx, %rbx

    .Lfind_next_newline_loop:
        cmpb $10, (%rbx)
        je .Lfind_next_newline_found

        incq %rax
        incq %rbx
        incq %rdx
        cmpq %rdx, %rcx
        je .Lfind_next_newline_exceeded_bounds_check

        jmp .Lfind_next_newline_loop

    .Lfind_next_newline_found:
        incq %rax
        ret

    .Lfind_next_newline_exceeded_bounds_check:
        ret

Ziemlich lang und ausführlich.

Hier kann man dann wieder ein rep-Konstrukt stattdessen verwenden:

current_line_length:
    movq -8(%rdi), %rdx
    movq -32(%rdi), %rbx
    addq %rbx, %rdx

    movq -24(%rdi), %rcx
    movq -32(%rdi), %rbx
    subq %rbx, %rcx
    movq %rcx, %rbx

    movq %rdx, %rdi
    movb $10, %al
    cld
    repne scasb

    subq %rcx, %rbx
    movq %rbx, %rax
    ret

Diesmal benutzen wir repne statt wie oben rep. Das wiederholt ebenfalls die Instruktion, bis %rcx 0 erreicht hat, prüft aber zusätzlich auch das „Zero Flag“, wie man bei Intel in der Dokumentation zum x86 Instruction Set nachlesen kann.

Jetzt muss man ein kleines Detail kennen: Wenn man ein cmp %rax, %rbx ausführt, zieht die CPU die beiden Register voneinander ab. Ein darauffolgendes je („jump if equal“) ist dann dafür gedacht, den Sprung durchzuführen, wenn die beiden Werte gleich waren – oder anders gesagt, wenn eine Subtraktion 0 ergeben hat. Dafür wird das Zero Flag benutzt. Wenn es also gesetzt ist (Subtraktion ergab 0), dann waren die beiden Werte gleich.

Wenn die Intel-Doku also von „repeat while ZF flag is clear“ spricht, ist das dasselbe, wie wenn man sagen würde „repeat while a comparison did not determine two values to be equal“. Deswegen ist auch repnz („repeat while not zero“) nur ein Alias für repne („repeat while not equal“).

Gut. Wir wissen jetzt also, was repne tut. Jetzt brauchen wir noch eine String-Operation ähnlich zu stosb, was wir in init_buffer_info() benutzt haben, aber diesmal soll sie den String nicht verändern, sondern nur Bytes daraus mit einem Wert vergleichen. Und das erledigt dann scasb: Das ist kurz für „scan string, length byte“. Das führt ein cmpb %al, (%rdi) aus und erhöhrt/verringert danach %rdi. Das repne drumherum wertet dann das Ergebnis dieses cmp aus (und prüft, ob %rcx die 0 erreicht hat).

Kurzum, wir haben nun ein strlen().

Da ist immer noch eine ganze Menge „Noise“ oben im Code um das eigentliche repne scasb drumherum. Trotzdem glaube ich, dass das so besser ist als der vorherige Spaghetti-Code. Man muss nur mit rep* vertraut sein.

Fazit

Reguläre Ausdrücke sind immer noch nicht in Sicht, aber wir haben schonmal ein paar der naiven Dinge des ersten cat behoben. Der Code beachtet jetzt auch die Rückgabewerte von Syscalls.

Oh, wenn man sich den Code von grep.asm anschaut, beachte man bitte die Git-Tags. Unter blogpost-step-2 findet man den die zu diesem Blogpost zugehörige Version.

Jetzt ist es also Zeit, test_match() zu schreiben.

vain
Normalzeit und Sommerzeit

In der EU wurde ja kürzlich eine Umfrage zur Zeitumstellung durchgeführt. Soll man das beibehalten oder abschaffen? Falls man es abschafft, welche Zeit soll dann gelten – dauerhaft Sommerzeit oder dauerhaft Normalzeit?

Grob 1% der EU-Bevölkerung hat an der Umfrage teilgenommen. Es war zwar eine offizielle Umfrage der EU, hat aber nicht den Charakter eines „Volksentscheids“, sprich, aus der Umfrage muss nicht zwangsläufig eine Gesetzesänderung folgen. Man wollte nur abfragen, „was die Leute darüber denken“. Selbstverständlich gibt es dazu jetzt viele hitzige und emotionale Diskussionen, die wir aber mal links liegen lassen. Stattdessen werfen wir mal einen Blick auf objektive Daten. Das ist ein Screenshot meines Desktops:

desktop

Wie bei vielen Leuten üblich, habe ich oben und unten ein paar Info-Leisten. In der rechten oberen Ecke sitzt dann dieses kleine Widget:

infofeld-sun

Es zeigt an, wann an meinem Standort die Sonne auf- und untergeht. Heute wäre das also etwa 7:02 Uhr für den Auf- und etwa 19:43 Uhr für den Untergang gewesen. Der rote Punkt zeigt „jetzt“ an und die dünne graue Linie den Zeitpunkt mit dem Sonnenhöchsstand. Es gibt da einen gewissen Spielraum für Fehler, weil ich nur vereinfachte Formeln für die Berechnung verwende. Die habe ich von hier:

https://lexikon.astronomie.info/zeitgleichung/

Trotzdem stimmen meine Zeiten recht gut mit denen von Online-Diensten wie zum Beispiel sonnenverlauf.de überein. Sagen wir mal, dass sie bis zu 3-5 Minuten danebenliegen können.

Zurück zur EU-Umfrage. Ich habe dasselbe Tool jetzt mal hergenommen und die Auf- und Untergangszeiten über das Jahr hinweg geplottet – aber dabei die Zeitumstellung deaktiviert. Man kann jetzt also sehen, wie es für ganzjährige Normalzeit (UTC+1) oder ganzjährige Sommerzeit (UTC+2) aussehen würde.

Konkret handelt es sich um das „sun“-Widget aus infofeld, libfaketime (oder man streut noch ein strptime ins infofeld ein) und ein bisschen Shell + ImageMagick + GIMP drumherum.

Das hier wäre dauerhaft UTC+1 im Südwesten Deutschlands:

normalzeit

(Der rote Punkt ist jetzt fest bei 12 Uhr.)

Und das wäre dauerhaft UTC+2:

sommerzeit

Der Vollständigkeit halber hier noch die aktuelle Situation mit der Zeitumstellung – Ende März von UTC+1 nach UTC+2 und Ende Oktober zurück nach UTC+1:

wechselzeit

Mir persönlich fällt es damit leichter, das Verhalten zu erfassen, als zum Beispiel mit Graphen wie diesen. Vermutlich wegen der runden Darstellung, die einer vertrauten Uhr ähnelt.

vain

Moin

10.09.2018

Hallo
ich bin der Aziz, komme ursprünglich aus Marokko und arbeite jetzt in Deutschland.
ich habe mich dazu entschieden mich in archlinux einzuarbeiten, weil ich gerne technisch rumexperimentiere, mehr über Linux verstehen möchte und gern die volle Kontrolle über mein System habe.
Außerdem finde ich pacman und das rolling release konzept sehr interessant. Nebenbei bemerkt ist auch das wiki sehr gelungen.

Ich hoffe ich werde noch viel Freude an Archlinux haben.

PS: Deutsch ist nicht meine Muttersprache und ich arbeite erst seit zwei Jahren in Deutschland, also seid bitte verständnisvoll für sprachliche Fehler ^^.

grep.asm – Schritt 1: Ein naives cat

Es gab kürzlich bei Computerphile einen kurzen Talk von Brian Kernighan über den Ursprung des Namens „grep“. Dort hat er auch erwähnt, dass ed und dann grep in PDP 11 Assembler geschrieben waren. Da dachte ich mir: Das wäre doch eine nette Übung, mal ein grep in x86-64 Assembler zu schreiben. :-)

Es wird ein paar Einschränkungen geben:

  • Nur Linux.
  • Das Programm wird nur von stdin lesen können. Eigenständig kann es keine Dateien öffnen (vielleicht später als Erweiterung …)
  • Es wird keine Kommandozeilenparameter geben.
  • Ich werde nur sehr einfache reguläre Ausdrücke einbauen.

Darüberhinaus bin ich mal ganz sicher nicht Ken Thompson. Das wird also eine Weile dauern. :-) Im Laufe der nächsten Monate werden voraussichtlich mehrere Blogposts dazu entstehen.

Wer Fehler findet oder Verbesserungen hat, immer her damit.

Ein naives, zeilenorientiertes cat

Der erste Schritt wird sein, ein zeilenbasiertes cat zu schreiben. Lies eine Zeile ein, dann gib sie wieder aus. Der folgende Code wird genau das tun. Der nächste Schritt ist dann, mal wieder mehr über x86-64 Assembler zu lernen und einige ineffiziente oder schlichtweg „blöde“ Code-Stellen zu beseitigen. Danach kann ich dann schauen, wie man überhaupt eine Regex-Engine programmiert und dann nur die matchenden Zeilen ausgeben lassen.

(Das hier ist natürlich noch nicht einmal cat, weil es nichts konkatenieren kann. Ist trotzdem klar, was ich meine.)

Das Programm wird keine Bibliotheken benutzen. Das schließt auch eine libc ein. Heißt also, dass wir Syscalls zur Kommunikation mit dem Kernel verwenden werden und sonst alles andere selbst schreiben. Wenn man also objdump auf die fertige Binary loslässt, soll nur unser eigener Code erscheinen.

Okay, hier ist das Hauptprogramm:

.text
.global _start

/* (0) */
_start:
    /* (1) */
    pushq %rbp
    movq %rsp, %rbp

    /* (2) */
    /*  -8(%rbp), 8 = pointer to current line (char *)
     * -16(%rbp), 8 = size of line buffer (size_t) */
    subq $16, %rsp

    /* Initialize our pointer with "NULL" and size with 0. */
    movq $0, -8(%rbp)
    movq $0, -16(%rbp)

    .Lreadwriteloop:
        /* (3) */
        /* Read a line. Memory management is done entirely by
         * readline(). */
        leaq -8(%rbp), %rdi
        leaq -16(%rbp), %rsi
        call readline

        /* Test if readline() returned 0. If it did, exit our loop. */
        cmpq $0, %rax
        je .Lreadwriteloop_end

        /* Print the line we've just read. */
        movq -8(%rbp), %rdi
        movq %rax, %rsi
        call print

        jmp .Lreadwriteloop

    .Lreadwriteloop_end:
        movq $0, %rdi
        call exit

Im Folgenden ein paar Erklärungen, falls du noch nie etwas mit Assembler zu tun hattest.

Bei (0) kann man das Label _start sehen. Hier beginnt das Programm. In C hat man es in aller Regel mit main() zu tun, das kommt aber aus der libc und das wollen wir hier nicht benutzen. Jedes Programm beginnt eigentlich bei _start und main() ist dann ein ziemlich dicker Wrapper drumherum. Zu dem Thema gibt es auch dieses ältere Posting.

Die zwei Instruktionen bei (1) bauen einen „Frame Pointer“ oder „Stack Frame“ auf. Auch dazu hatte ich schonmal etwas geschrieben – das war damals noch 32-Bit-Code, funktioniert aber bei 64 Bit im Wesentlichen genauso. Hier drüben gibt es nochmal eine Erklärung in Englisch dazu.

Die grundlegende Idee eines Frame Pointers ist, ein Register zu haben, das die Basisadresse des aktuellen Stack Frames enthält. Dieser Wert ist für die Laufzeit der aktuellen „Funktion“ fest. Dafür wird in aller Regel %rbp verwendet. Adressen relativ dazu können dann auf „Variablen“ auf dem Stack zeigen.

Beispiel:

movq $0, -8(%rbp)

movq heißt, „kopiere ein Quad Word“ (das sind bei uns 8 Bytes). Welche 8 Bytes? Ich benutze hier ein „immediate value“ und zwar eine 0. Wo soll das hinkopiert werden? In den Speicher, der vom Wert in %rbp adressiert wird – minus 8 Bytes. Der Stack in Linux wächst von hohen Adressen hin zu niedrigen Adressen, daher das „minus“.

Bei (2) wird dann die „Größe“ des Stack Frames verändert und dadurch Platz gemacht für unsere Variablen. Dabei wird nach meinem Verständnis nichts tatsächlich „allokiert“, aber zukünftige push-Instruktionen werden beeinflusst, weil diese relativ zu %rsp arbeiten. Wenn wir also einen Wert von %rsp abziehen, dann können wir so tun, als hätten wir ganz viele Werte auf den Stack gepusht, ohne die tatsächlichen push-Instruktionen auch auszuführen. Wenn irgendwelcher fremder Code dann eben doch ein push ausführt, wird er unseren Speicher nicht überschreiben. Auf der anderen Seite können wir selbst auf diese Weise den Speicher direkt ansprechen und zwar mit sowas wie -48(%rbp).

Heißt also, wir benutzen hier zwar Speicher auf dem Stack, aber ohne die Semantiken eines echten Stacks. Einfach, weil es einfacher ist. Es ist für uns linearer Speicher und wir können quasi jedes Byte individuell ansprechen.

So viel zur Wiederholung über Stack Frames.

Die Schleife um (3) herum wird dann eine Zeile lesen, prüfen, ob das tatsächlich funktioniert hat, und die Zeile dann ausgeben. Die Funktionen exit, print und readline sind noch nicht definiert, zu denen kommen wir gleich.

„Eine Funktion aufzurufen“ heißt, dass man sich an Calling Conventions halten sollte. Das ist ein absolutes Muss, wenn man Bibliotheksfunktionen aufrufen oder von ihnen aufgerufen werden möchte. In unserem Programm müssen wir uns aber nicht ganz so strikt daran halten, weil immer nur unser eigener Code läuft. Zum Beispiel habe ich ignoriert, dass manche Register von aufgerufenen Funktionen nicht überschrieben werden sollten. Ich versuche trotzdem, mich an die übliche cdecl-Konvention zu halten.

exit

Nicht so schwer:

exit:
    /* in: %rdi, exit code */

    /* 60 = _exit(), %rdi already contains the exit code */
    movq $60, %rax
    syscall

Einfach nur ein Syscall. Das ist jetzt der Punkt, an dem man alle Hoffnung aufgeben kann, dass dieses Programm noch woanders als unter Linux laufen könnte – es sei denn, dein Betriebssystem benutzt zufällig auch die Nummer 60, um einen Syscall zu beschreiben, der das Programm beendet.

Betrifft natürlich alle Syscalls. Die Nummerierung muss passen und die Parameter natürlich auch. Selbstverständlich muss man sich auch an die korrekte Calling Convention halten. Die ist bei Linux für Syscalls ein kleines bisschen anders als im Userland (siehe A.2.1 im SysV ABI draft und dieses alte Blogposting).

print

Ebenfalls nicht kompliziert:

print:
    /* Print the given number of bytes.
     *
     * in:
     *   %rdi, pointer to buffer (char *)
     *   %rsi, length of string (size_t) */

    /* Just shuffle arguments, call write(1, &buffer, len) (syscall #1) */
    movq %rsi, %rdx
    movq %rdi, %rsi
    movq $1, %rax
    movq $1, %rdi
    syscall
    ret

Eigentlich ist das auch nur ein Wrapper um den write()-Syscall von Linux mit einem festen Wert 1: File Descriptor Nummer 1 wird als stdout angenommen.

Ich hätte mir vielleicht exit und print sparen können und direkt im Hauptprogramm die Syscalls benutzen können. Habe ich nicht gemacht, weil ich das Programm etwas abstrakter und möglicherweise leichter verständlich halten wollte.

readline

Hier wird es jetzt interessant.

Mit etwa 100 Zeilen Code ist das die bisher längste Funktion. Wäre sie in C geschrieben, hätte sie diese Signatur:

size_t readline(char **buffer, size_t *buffer_size);

Der erste Parameter ist ein Pointer auf einen „String“, den wir aber nicht NUL-terminieren, weil wir faul sind. Der zweite Parameter ist ein Zeiger auf irgendwelche 8 Bytes, die die derzeitige Größe des Puffers speichern sollen. Beide Variablen leben auf dem Stack des Hauptprogramms – sie wurden bei Schritt (2) allokiert.

Unsere Implementierung von readline wird auf dem Heap dynamisch Speicher allokieren. Dort wird dann eine ganze Zeile abgelegt. Irgendwann in der Zukunft kann die (noch hypothetische) Regex-Engine dann auf diesem Puffer arbeiten. Für den Moment wird sein Inhalt aber einfach nur mittels print ausgegeben, nachdem wir readline wieder verlassen haben.

Um Speicher auf dem Heap zu allokieren, kann man unter Linux den brk()-Syscall benutzen. Ein brk(0) gibt der derzeitige Adresse des „Program Break“ zurück. Ruft man das zum ersten Mal auf, kriegt man eben irgendeine Adresse. Danach kann man aber brk(first_result + some_size) aufrufen, um damit den Program Break zu verschieben – und dadurch entsteht dann der Heap. Das macht in C die Funktion malloc() (manchmal) hinter den Kulissen. Eigentlich funktioniert das insgesamt recht ähnlich zu unserem Stack, allerdings in die andere Richtung. In Linux wächst der Heap in Richtung höherer Adressen.

Was wird readline also machen?

  1. Einen Puffer verwalten, um die gelesenen Daten zu speichern.
  2. Daten von stdin lesen und in diesem Puffer ablegen.
  3. Prüfen, ob wir gerade ein Newline-Zeichen gelesen haben, was dann das Ende der aktuellen Zeile darstellen würde.
  4. Am Ende die Zahl der gelesenen Bytes zurückgeben.

So sieht die tatsächliche Funktion dann aus:

readline:
    /* Read a line (terminated by \n or EOF) from stdin.
     *
     * in + out:
     *   %rdi, pointer to pointer to buffer (char **)
     *   %rsi, pointer to current size of buffer (size_t *)
     *
     * out:
     *   %rax, length of string read (including \n), 0 if EOF */
    pushq %rbp
    movq %rsp, %rbp

    /*  -8(%rbp), 8 = pointer to pointer to buffer (char **)
     * -16(%rbp), 8 = pointer to size of buffer (size_t *)
     * -24(%rbp), 8 = length of string
     * (other 8 bytes are alignment) */
    subq $32, %rsp

    movq %rdi, -8(%rbp)
    movq %rsi, -16(%rbp)
    movq $0, -24(%rbp)

    .Lreadline_next_byte:
        /* Check if there is still room in the buffer */
        movq -16(%rbp), %rbx
        movq (%rbx), %rax
        cmpq %rax, -24(%rbp)
        je .Lreadline_increase_buffer
        .Lreadline_buffer_large_enough:

        /* read(stdin, &(buffer + len), 1) (syscall #0, stdin = fd 0) */
        movq -8(%rbp), %rbx
        movq (%rbx), %rsi
        movq -24(%rbp), %rax
        addq %rax, %rsi

        movq $0, %rax
        movq $0, %rdi
        movq $1, %rdx
        syscall

        /* EOF? */
        cmpq $0, %rax
        je .Lreadline_exit

        /* We read something, so increase the counter. */
        movq -24(%rbp), %rax
        incq %rax
        movq %rax, -24(%rbp)

        /* \n found? */
        movq -8(%rbp), %rbx
        movq (%rbx), %rsi
        movq -24(%rbp), %rax
        decq %rax
        addq %rax, %rsi
        cmpb $10, (%rsi)
        je .Lreadline_exit

        jmp .Lreadline_next_byte

    .Lreadline_increase_buffer:
        /* Get address to base of heap if pointer is still NULL. */
        movq -8(%rbp), %rbx
        movq (%rbx), %rax
        cmpq $0, %rax
        je .Lreadline_initial_buffer_allocation
        .Lreadline_buffer_initially_allocated:

        /* Increase buffer by 32 bytes and call brk(base + new_size).
         * This includes saving the new buffer size to the memory
         * pointed by -16(%rbp). */
        movq -8(%rbp), %rbx
        movq (%rbx), %rdi
        movq -16(%rbp), %rbx
        movq (%rbx), %rax
        addq $32, %rax
        movq %rax, (%rbx)
        addq %rax, %rdi

        movq $12, %rax
        syscall
        jmp .Lreadline_buffer_large_enough

    .Lreadline_initial_buffer_allocation:
        /* brk(0) (syscall #12) to get base of heap */
        movq $12, %rax
        movq $0, %rdi
        syscall
        movq -8(%rbp), %rbx
        movq %rax, (%rbx)
        jmp .Lreadline_buffer_initially_allocated

    .Lreadline_exit:
        movq -24(%rbp), %rax
        leave
        ret

Das ist die „Schönheit“ von Assembler. Es ist echt lang, aber eigentlich auch echt einfach. Es ist nur ein Haufen von „kopiere Daten“, „addiere oder subtrahiere Zahlen“, „vergleiche Werte“, „springe irgendwo hin“ und „sezte einen Syscall ab“. Und eigentlich solltest du auch in der Lage sein, zu verstehen, was hier vor sich geht. Über Stack Frames haben wir schon gesprochen. Über brk() auch. Mit dem read()-Syscall solltest du eh vertraut sein. Und das war’s auch schon. Mehr Magie steckt da nicht drin.

Vermutlich ist es aber trotzdem schwer zu verstehen. Vor allen Dingen werden viele Adressen hin- und hergeschubst. Anfänger haben manchmal Probleme, einfache Pointer in C zu verstehen, und hier haben wir jetzt doppelte Pointer in Assembler. Trotzdem glaube ich, dass das eine lohnenswerte Übung ist: Wenn man erstmal verstanden hat, dass es sich einfach nur um Speicheradressen handelt und dass das ein fundamentaler und eigentlich simpler Teil des alltäglichen Programmmierens ist, dann werden einem Pointer in C ziemlich trivial vorkommen.

Trotzdem nochmal genauer:

In _start enthält das Register %rbp eine Adresse. Bei dieser Adresse beginnt der Stack. Das ist also schon ein „Pointer“ und mit (%rbp) kann man ihn dann dereferenzieren. -8(%rbp) heißt dann, dass 8 vom Wert in %rbp abgezogen werden soll (ohne %rbp zu verändern) und dann das Ergebnis davon für die eigentliche Operation zu benutzen.

leaq -8(%rbp), %rdi führt dieselbe Berechnung durch – subtrahiere 8 vom Wert in %rbp, ohne den Registerinhalt zu ändern – und speichert das Ergebnis dieser Berechnung in %rdi. lea steht für „load effective address“. Dasselbe Ergebnis könnte man erreichen, indem man den Wert von %rbp in ein anderes Register kopiert und dort die 8 subtrahiert, aber lea ist schneller und einfacher. In jedem Fall steht am Ende in %rdi eine Speicheradresse, unter der man eine weitere Adresse findet – ein „double pointer“.

%rdi wird also im Hauptprogramm (_start) gesetzt und dann als Parameter für readline benutzt. In readline selbst speichern wir dann eine Kopie dieses Wertes auf dem lokalen Stack. Wenn wir dann tatsächlich mit diesem Pointer arbeiten wollen, holen wir den Wert vom Stack (das ist nun das ursprüngliche %rdi) und dereferenzieren einmal. Damit bleibt ein einfacher Pointer übrig, ein „char *“, den wir dann read() übergeben können.

Weitere Anmerkungen:

  • Quasi keine Fehlerbehandlung. Das ist schlecht und muss behoben werden.
  • Die Labels, die mit .L anfangen, sind lokale Symbole in GNU as. Ich benutze sie als Jump-Labels innerhalb einer Funktion und die sind dann nicht mehr im Weg, wenn man mit GDB mal debuggen muss.
  • leave ist das Gegenteil von pushq %rbp; movq %rsp, %rbp. Es verwirft also den aktuellen Stack Frame. Vielleicht ist aufgefallen, dass exit und print gar kein Stack Frame haben, also gibt es hier auch kein leave. exit hat noch nicht einmal ein ret, weil das Programm niemals aus dem Syscall zurückkehrt.
  • Man kann bei mov nicht beliebige Quellen und Ziele angeben. Deswegen muss ich oft einen Wert erst vom Stack in irgendein Register kopieren und dann dort nochmal dereferenzieren. Ich hoffe, das ist so eine Sache, die man leicht optimieren kann.
Fazit

Den kompletten Code gibt es hier:

grep.asm

So, das ist jetzt nun mal gar nicht flott. Wir lesen Byte für Byte, was vermutlich das langsamste ist, was man tun kann. Ich habe noch gar nichts „mikro-optimiert“. Um ehrlich zu sein, es ist hauptsächlich einfach so runtergeschrieben worden. Deswegen heißt das Ding bis jetzt „ein naives cat“. Es ist zwar in Assembler geschrieben, aber das heißt eben noch lange nicht, dass es „toll und schnell“ ist. Das muss ich mir als nächstes anschauen.

Fortsetzung: Diese Serie geht in grep.asm – Schritt 2: Ein besseres cat weiter.

vain

Die Aktualisierung auf libutf8proc-2.1.1-3 erfordert das erzwungene Überschreiben von /usr/lib/libutf8proc.so.2:

pacman -Syu --overwrite usr/lib/libutf8proc.so.2
Zehn Jahre Arch Linux

Logbuch: Vor etwa zehn Jahren bin ich von Ubuntu zu Arch Linux gewechselt.

Seitdem habe ich aufgehört, mir wegen Upgrades Sorgen zu machen. Nicht ein einziges Mal hat mir ein Upgrade einen Rechner zerlegt. Nur einmal habe ich Arch in 2011 neuinstalliert: Das war der einfachste Weg, um von 32 Bit zu 64 Bit zu kommen. (Hatte neue Hardware gekauft und der vorherige Intel Core Duo T2300 hat einfach noch kein 64 Bit unterstützt gehabt.) Oder vielleicht war ich auch nur faul, denn es gibt eine Anleitung, um zwischen Architekturen zu migrieren, die es auch schon im Jahre 2011 gab.

Quasi alle Updates verlaufen völlig problemfrei. Meine Arch-Kisten bedürfen fast keiner Wartung außer

  1. die Arch-News lesen,
  2. (Bonuspunkte, wenn man ein Auge auf den Bug-Tracker und die Mailinglisten hat),
  3. pacman -Syu laufen lassen,
  4. sich um .pacnew-Dateien kümmern
  5. und verwaiste Abhängigkeiten (pacman -Qdt) aufräumen, falls einem langweilig ist.

Das ist so viel einfacher, als sich mit riesigen Release-Upgrades auseinanderzusetzen.

An alle Arch-Entwickler, Trusted-Users, Bug-Jäger und wer sonst noch beteiligt ist: Vielen Dank. Ihr leistet großartige Arbeit!

Keep rolling!

vain
Ist mein Dateisystem für 2038 bereit? (ext4)

Im Jahre 2038 werden Timestamps auf Basis von 32-Bit-Integern überlaufen. Sehr viele Timestamps sind so gespeichert, also wird es ein Problem werden. Oder? 2038 ist noch 20 Jahre hin, bis dahin sollten wir das ja wohl auf die Reihe gekriegt haben. Tja, naja, ich kann mich noch gut daran erinnern, als der Satz ging: „2038 ist noch 30 Jahre hin, bis dahin …“. Und ich kann mich auch noch gut an die Panik um das Jahr 2000 herum erinnern. Das geht alles schneller, als man denkt.

Software zu aktualisieren, ist eine Sache. Dateisysteme sind aber immer etwas träger, glaube ich – es ist halt lästig, so ein On-Disk-Format zu ändern.

Schauen wir mal schnell bei ext4 nach.

Der aktuelle Stand bei ext4

Erzeugen wir mal ein Dateisystem und tun wir so, als wären wir schon in der Zukunft.

$ dd if=/dev/zero of=fs bs=1G count=1
$ mkfs.ext4 fs
$ mkdir m
# mount -o loop fs m
# touch -d '2140-01-01 12:00:00' m/future
# umount m

Das remounten wir dann, um sicherzugehen, dass wir tatsächlich mit den Timestamps arbeiten, die auf der Platte gespeichert sind – und nicht mit Werten, die von Linux nur gecached wurden.

# mount -o loop fs m
$ ls -l m
# umount m

Was mal schnell auffällt: ls zeigt die korrekte Zeit und das korrekte Datum an. Darauf kommen wir später nochmal. Aber hey, das sieht ja schonmal gut aus!

Schauen wir noch ein bisschen genauer hin. Mit debugfs kann man sich die Rohdaten auf der Platte anschauen.

$ debugfs fs
debugfs:  ls
 2  (12) .    2  (12) ..    11  (20) lost+found    12  (4040) future   
debugfs:  inode_dump future
0000  a481 e803 0000 0000 30db c23f d294 2f5b  ........0..?../[
0020  30db c23f 0000 0000 6400 0100 0000 0000  0..?....d.......
0040  0000 0800 0100 0000 0af3 0000 0400 0000  ................
0060  0000 0000 0000 0000 0000 0000 0000 0000  ................
*
0140  0000 0000 776f 4da0 0000 0000 0000 0000  ....woM.........
0160  0000 0000 0000 0000 0000 0000 6292 0000  ............b...
0200  2000 238e 2c3c dd7d 0100 0000 0100 0000   .#.,<.}........
0220  a394 2f5b f448 f37d 0000 0000 0000 0000  ../[.H.}........
0240  0000 0000 0000 0000 0000 0000 0000 0000  ................
*

Soso. Wir interpretiert man das? Um ehrlich zu sein, weiß ich nicht, wo es eine offizielle Dokumentation dazu gibt, wenn man mal vom recht kompakt kommentierten ext4-Quellcode absieht. Es gibt aber einen Artikel im ext4-Wiki auf kernel.org – der kann ja so verkehrt nicht sein.

Man beachte an der Stelle, dass die Offsets von debugfs oktal notiert sind, nicht hexadezimal.

Okay, wir suchen also das Datum der letzten Änderung der Datei, i_mtime. Das steht bei Offset 0x10 (020) und wird als 32-Bit-Integer in Little Endian genannt. Das ist diese Byte-Sequenz:

30db c23f

Dreht man’s wegen Endianness um, dann bekommt man:

0x3fc2db30

Das kann nicht alles sein, weil wir das Jahr ja auf 2140 gesetzt haben (man rechne 0x3fc2db30 einfach mal in eine Dezimalzahl um, dann sieht man schnell, wie klein die ist). Stellt sich raus, dass noch zwei weitere Bits benutzt werden, um den Timestamp zu erweitern. Gespeichert sind sie in i_mtime_extra bei Offset 0x88 (0210) – das sind nochmal 32 Bit:

0100 0000

Nach Endianness hat man hier nur die Zahl 1.

Im Wiki steht dann, wie man die beiden Felder zusammenfügt:

https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Inode_Timestamps

Sprich, wir sind beim zweiten Feld eh nur an den zwei niedrigsten Bit interessiert.

Lange Rede, kurzer Sinn: Wir müssen nochmal 0x100000000 auf das ursprüngliche 0x3fc2db30 draufaddieren, womit wir dann bei dezimal 5364702000 wären. Und das stimmt dann auch:

$ date -d '2140-01-01 12:00:00' +%s
5364702000

tl;dr: ext4 opfert zwei Bit des Nanosekunden-Felds und fügt sie zum Timestamp hinzu. Das wird bis zum Jahre 2446 funktionieren.

Andere Größen relevanter Datentypen

Man konnte oben sehen, dass ls schon ordentlich das Jahr 2140 angezeigt hat. Das impliziert, dass schon Arbeit investiert wurde, um das Userland mit dem Jahr 2038 umgehen zu lassen. Einer der wichtigsten Datentypen ist time_t. Wie groß ist der auf heutigen amd64-Systemen?

#include <stdio.h>
#include <time.h>

int
main()
{
    printf("%zd\n", sizeof (time_t));
    return 0;
}

Groß genug, 64 Bit:

$ gcc -Wall -Wextra -std=c99 -o foo foo.c && ./foo
8

Ist es garantiert, dass der so groß ist? Weiß ich nicht so richtig, weil der C-Standard hinter einer Paywall eingemauert ist. Ein Draft, den ich gefunden habe, erzählt etwas von „implementation-defined“. Meh.

time_t wird an einigen Stellen benutzt, zum Beispiel im struct timespec, was dann wiederum vom stat-Syscall (heute) verwendet wird. Sprich, solange das VFS in Linux intern Datentypen benutzt, die mit Daten nach 2038 umgehen können, sollte das Userland keine Probleme haben. Schätze ich. :-)

vain

Hallo zusammen,

nachdem mein erstes Netbook, ein Acer Aspire One, viele Jahre mit Archlinux seinen treuen Dienst geleistet hat, wurde mangels Zeit und Lust, meine restliche Familie bei der Hand zu führen, ein Chromebook angeschafft, dessen einziger Zweck es war als unkomplizierte Surfmaschine zu dienen.
Das Aspire One läuft zwar noch, aber mit dem kleinen Bildschirm ist es nicht wirklich komfortabel und so wurde es immer mehr vernachlässigt, während das Chromebook weitgehend seinen Zweck erfüllte. Nun hat Google aber den Support für das gute Stück aufgekündigt und plötzlich kamen auch die ersten Abstürze, was vorher eigentlich kein Thema war.
Grund genug für mich das Chromebook, welches ohnehin von den anderen dank Smartphone und Tablet kaum noch genutzt wird, auf ein zukunftssicheres OS umzustellen. Da Archlinux schon auf meinem alten Desktop und dem Netbook hervorragende Dienste geleistet hatte, viel die Wahl nicht schwer. Besonders als ich das Chromebook auch noch im umfangreichen Wiki gefunden hatte.
Abgesehen von der Fummelei, das Gerät überhaupt für ein anders OS bootfähig zu machen, war ich doch erstaunt wie gut die Installation geklappt hat. Die grauen Zellen sind zwar etwas eingerostet, aber nach und nach kommt die Erinnerung zurück und es hat mal wieder Spaß gemacht etwas tiefer im System zu basteln.

In diesem Sinne ein großes Dankeschön an alle Beteiligten für dieses wunderbare System.

Der SMTP Server Postfix bietet sehr umfangreiche Möglichkeiten zur Reduzierung der Anzahl von Spam-Mails die es bis zum User-Account schaffen. Dieser Artikel befasst sich mit dem Szenario wie man bei verschiedenen Diensten im Internet eine spezielle E-Mail Adresse verwenden und nach einem Leak schon durch Postfix an diese Adresse gesendete …

Thorsten Töpper

Pacman 5.1.0

29.05.2018

Das neue Paket zu pacman enthält nicht mehr alle Tools, die im Vorgängerpaket noch enthalten waren. Diese wurden in das Paket pacman-contrib ausgelagert.

Edit: Link korrigiert.

DSGVO

12.05.2018

In knapp 2 Wochen ist es soweit, denn am 25.05.2018 tritt die Datenschutz-Grundverordnung (DSGVO) in Kraft.
Ich habe mich nun auch mal damit auseinandergesetzt und aus den ganzen Aenderungen Konsequenzen gezogen:

  • Alle iframes entfernt (YouTube, Vimeo, GitHub, Twitter, ...) und durch Links ersetzt
  • Unter Datenschutz ein bisschen ausfuehrlicher beschrieben, was ich speichere
  • Meine Content Security Policy (CSP) angepasst (u.a. mit Hilfe des CSP Evaluators von Google und ReportURI)
  • Den Header Referrer-Policy von no-referrer-when-downgrade zu strict-origin-when-cross-origin geaendert (Erklaerung)

Damit ist meine Website noch ein bisschen schneller und datenschutzfreundlicher geworden und ihr koennt nun selbst entscheiden, ob ihr euch das Verlinkte ansehen wollt oder nicht.
Da ja nun auch keine externen Videos mehr eingebunden werden, koennen diese trotzdem recht datenschutzfreundlich angeguckt werden, z.B. mit youtube-dl.

Es steht allerdings noch die Anpassung/Anonymisierung der IP-Speicherung durch Nginx aus.
Allerdings bin ich mir hier noch nicht sicher, ob und wie ich diese durchfuehre.

Fuer alle, die etwas mehr einsetzen als ich (z.B. Cookies), gibt es hier ein paar Links (auch die weiteren Links beachten!):

Interessant ist in dem Zusammenhang das Tool Webbkoll, was eine URL auf diverse Dinge, z.B. TLS, Referrer, Cookies, CSP und Third-parties testet.

Ergebnis fuer yhaupenthal.org von Webbkoll

Angestiftet durch den Hinweis "Referrers partially leaked" hatte ich kurzzeitig die Policy auf no-referrer gesetzt, mich dann aber doch wieder umentschieden, da so fremde Websites nicht einsehen koennen, dass Leute von mir kommen, was ich dann zwecks Bekanntheitsgrad vermeiden moechte. (Convince me otherwise!)

Die letzten Jahre war es hier recht ruhig. Das hatte verschiedene Gründe, unter anderem weil ich mich mit dem zuvor verwendeten octopress nicht wirklich anfreunden konnte. Ruby ist keine Sprache, welche ich häufig verwende und da octopress nach Aktualisierungen auf meinen Systemen leider häufiger Probleme mit inkompatiblen Gems hatte, war …

Thorsten Töpper

TL;DR: If the remote server allows Restricted Admin login, it is possible to login via RDP by passing the hash using the native Windows RDP client mstsc.exe. (You’ll need mimikatz or something else to inject the hash into the process)

On engagements it is usually only a matter of time to get your hands on NTLM hashes. These can usually be directly used to authenticate against other services / machines and enable lateral movement. Powershell / PSExec, SMB and WMI are usual targets to pass the hash to, but it is also possible to use it to establish a RDP session on a remote host. Searching the Internet on how to do this unfortunately always leads to using xfreerdp, but I wasn’t able to find anything on the Internet regarding how to do this directly using the provided RDP client mstsc.exe, so I had to find out on my own.

Don't let your dreams be dreams

How does it work?

Interestingly, it was quite easy to find out, so here is how to do it with mimikatz (you’ll need local admin):

sekurlsa::pth /user:<user name> /domain:<domain name> /ntlm:<the user's ntlm hash> /run:"mstsc.exe /restrictedadmin"

This will open a new RDP window. If it still shows the user you are currently logged on with, just ignore it - everything will just work ;-)

Enter the domain name / IP of the target server and if the target server allows Restricted Admin Mode, you will be logged in, otherwise the server will tell you that you are not allowed to log in.

Why does it work?

RDP Restricted Admin Mode builds upon Kerberos. Taking a look at the network traffic, one can see that the RDP client requests a ticket on behalf of the impersonated user which is no problem since the hash is all we need to authenticate against Kerberos.

Restricted Admin Mode is disabled, what can I do?

A registry key controls if a server accepts Restricted Admin sessions. If you have the NTLM hash of a user that has privileges to set registry keys, you can use for example Powershell to enable it and log in via RDP afterwards:

mimikatz.exe "sekurlsa::pth /user:<user name> /domain:<domain name> /ntlm:<the user's ntlm hash> /run:powershell.exe"

A new Powershell window will pop up:

Enter-PSSession -Computer <Target>
New-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Lsa" -Name "DisableRestrictedAdmin" -Value "0" -PropertyType DWORD -Force

Now, your RDP should work fine.

Young Luke Skywalker It's working gif

Die Aktualisierung des Paketes js52 erfordert einen manuellen Eingriff, da das Vorgängerpaket die Datei /usr/lib/libmozjs-52.so.0 nicht enthalten hat. Dies wurde jetzt nachgeholt, aber pacman kann Dateien, die ihm nicht gehören, nicht automatisiert löschen. Daher sollte man dies manuell tun.

Als Root

rm /usr/lib/libmozjs-52.so.0 

Da bei der Aktualisierung auf glibc 2.27-2 die Unterstützung für NIS und NIS+ entfernt wurde, erfordert sie unter Umsständen manuelle Eingriffe. Die mit dem Paket filesystems kommende Datei /etc/nsswitch.conf ist auf diese Veränderung hin schon angepasst, aber eventuell muss hier eine bestehende, angepasste Datei bit der entsprechenden pacnew-Datei zusammengeführt werden, und dies besser vor der Aktualisierung.

NIS-Funktionalität kann mittels des Paketes libnss_nis installiert werden. Zu NIS+ gibt es kein offizielles Paket mehr.

pam 1.3.0-2 enthält keine pam_nix2-Module und keine symbolischen Kompatibilitäts-Verküpfungen zu pa_unis_*.so mehr. Konfigurationsdateien unter /etc/pam.d sollten daher untersucht werden, und darin alle Module, die entfernt wurden, durch pam_unix.so ersetzt werden. Benutzer von pam_unix2 sollten dabei auch Passwörter ändern. Dies ist nicht nötig, wenn nur die Standards aus dem pambase-Paket verwendet werden.

Seit heute kann man Fragen zu Arch Linux32 auch auf Deutsch stellen. Die (wenigen) Deutschsprachler unter den Entwicklern (oder auch Benutzern) werden sich bemühen, die Fragen zu beantworten.

melde mich zurück

09.03.2018

Guten Abend Gemeinschaft!

Ich melde mich zurück. Sieben Monate war ich nicht mehr an meinem Arch Linux. Stolz war ich dennoch darüber, es weit geschafft zu haben. Gestern saß ich wieder den ganzen Abend am Netbook und versuchte mich erneut am Arch. Aber ich bin gescheitert, am Fenstermanager. Frustriert am ende des Abends, schaute ich mir die fertige Dristo "Archbox" an, um mir ein Bild von "openbox" zu machen.

Warum ich mich jetzt erst wieder melde? Vor sieben Monaten, kurz nach meinem ersten erfolgreichen Versuch mit Arch, bekamen wir Zuwachs, sodass andere Prioritäten im Vordergrund standen und weiterhin bestehen bleiben.

Nichts desto trotz habe ich wieder Lust bekommen, Arch zu installieren, in der Hoffnung, das es demnächst mit meinem System klappt. Aber letzten Endes geht es mir um das Verständnis.

Parallel warte ich das Notebook meiner Frau mit einem Ubuntu Budgie.

Dies war ein kurzer Einblick in meine momentane Situation und wünsche euch einen schönen Abend und ein schönes Wochenende!

Grüße aus Großbeeren bei Berlin

Die Version 1.6.0-1 des Paketes zita-resampler weist einen Fehler auf.  Es fehlte im Paket ein symbolischer Link auf eine shared library, die in 1.6.0-2 wieder enthalten ist.

Aktualisiert man also von 1.6.0.1 auf 1.6.0-2, bekommt man einen Konflikt bezüglich

/usr/lib/libzita-resampler.so.1

Diese Datei sollte manuell gelöscht werden.

noch nen Neuer

11.02.2018

moinsen
ich hab mich nun endlich hier mal registriert, weil ich das Wiki mehrmals auf den Kopf gestellt habe, mir schon etliche Beiträge reingezogen habe, und dadurch auch schon mehrfach ein Arch auf den Rechner gezogen habe. Somit fühle ich mich hier gut aufgehoben, ohne Fragen zu stellen, was aber leider nicht ganz ausgeschlossen ist.....
Wie ich zu Arch kam ? Ganz einfach - durch Zufall.... big_smilebig_smile
Zuvor hatte ich Mint, Fedora,Suse,Debian und noch so einige Distros und deren Forks getestet - letztendlich blieb ich bei Arch hängen. Der Hauptgrund liegt wohl am RR, dem AUR und vor allem an pacman. Sowas bot mir bisher noch keinen Distri.
Gelernt habe ich Einiges und bin noch lange nicht fertig damit. Eines muss ich aber gestehen - nach der 4. oder 5. Install, weil ich mir immer mal das Sys zerschiesse, wegen zu viel Experimentierfreudigkeit, griff ich dann auf den "ZEN-Installer" zurück. Yop! - soviel Bequemlichkeit gönne ich mir, weil ich nach nen Crash das Sys enorm einfacher und schneller wieder auf der Platte habe.
Muss dazu aber noch sagen, dass ich im Rechner 2 SSD und 2 HDD am werkeln habe. Auf einer SSD ist ein W7, was Frau und meine Wenigkeit auch zum Arbeiten brauchen, da es "noch" unverzichtbar ist. Auf der 2. SSD ist ein händisch aufgespieltes Arch mit dem KDE -Plasma Desktop, was ich auch zum Arbeiten nutze. Auf einer HDD ist mein experimentier-Arch, was öfters mal den Bach runter geht - und auf der letzten HDD ist mein Datengrab.
Jedes BS hat seinen eigenen Boot-Loader, sodass ich da relativ entspannt testen kann.....
Auf alle Fälle muss ich das Wiki ganz groß loben - es ist das Beste, was ich bisher zu Gesicht bekam ! Allerdings ist das "Buntu-Wiki" auch nicht von schlechten Eltern und mit beiden zusammen hab ich schon viele Problemchen lösen können.
Ich freu mich jedenfalls auf weitere Experimente..... lol

U2F in Firefox 57 aktivieren