Windows Archives - Technik - Blogbasis.net https://technik.blogbasis.net/category/windows Die Basis des freien Wissens – Technik Thu, 19 May 2016 23:05:34 +0000 de hourly 1 https://wordpress.org/?v=6.8.1 Windows ohne USB-Stick & CD-Laufwerk installieren https://technik.blogbasis.net/windows-ohne-usb-stick-cd-laufwerk-installieren-20-05-2016 https://technik.blogbasis.net/windows-ohne-usb-stick-cd-laufwerk-installieren-20-05-2016#respond Thu, 19 May 2016 23:05:34 +0000 http://technik.blogbasis.net/?p=1405 Ein Bekannter hat sich letztens einen neuen Laptop gekauft, allerdings ohne CD-Laufwerk und ohne vorinstalliertes Betriebssystem. Die Windows-auf-USB-Stick-Installation klappte nicht, also musste eine alternative Lösung her. Diese werde ich in diesem Blogpost erläutern.

Die Situation

Funfact vor weg: Notebooksbilliger.de hat zum CD-laufwerklosen Laptop eine Treiber-CD mitgeliefert. Der Sinn ergibt sich mir bis jetzt nicht. Zudem war auf dem „betriebssystemlosen“ Laptop ein Linpus-Linux (Chinalinux) vorinstalliert.

Nichtsdestotrotz war das Ziel ein Windows auf diesem Laptop zu installieren. Der Versuch eine Windows-ISO auf einem USB-Stick bootbar zu machen klappte zunächst, jedoch meldete die Installationsroutine der USB-Stick könne nicht gefunden werden.

Der Workaround

Da es schon spät abends war, hatte ich auch keine Lust mehrere solcher Windows-USB-Sticks zu erstellen. Eine alternative Lösung musste her.

Da erinnerte ich mich an den Google-CTF, in dem man mit dem NBD-Protokoll arbeiten musste.

Folgende Idee kam mir dabei in den Kopf:

  • Auf beiden Laptops Linux starten
  • Festplatte über NBD freigeben und einhängen
  • Mit VirtualBox Windows auf die freigegebene Festplatte installieren

Das hört sich kompliziert an, hatte am Ende aber einige Vorteile, da Windows 7 leider keine passenden USB- bzw. Netzwerktreiber für den Laptop mitbrachte:

  • USB-Zugriff über VirtualBox
  • Netzwerk-Zugriff über VirtualBox
  • CD-Zugriff über VirtualBox

Die Umsetzung

Zunächst bootet man auf zwei Rechnern Linux. In meinem Fall habe ich für den neuen Laptop (A) ein Archlinux-Livesystem genutzt, da auf meinem Laptop (B) bereits Archlinux läuft. Andere Distributionen sollten für dieses Vorhaben auch nutzbar sein, allerdings muss wahrscheinlich ein andersnamiges Paket installiert werden.

Der erste Schritt besteht darin, auf beiden Laptops NBD zu installieren. Unter Archlinux heißt das Paket „nbd“.

sudo pacman -S nbd

Als nächstes baut man ein Netzwerk zwischen beiden Rechnern auf. Dazu habe ich diese mit einem LAN-Kabel direkt verbunden. Es ist aber auch möglich einen Router und automatische IP-Konfiguration (DHCP) zu nutzen.

Auf Laptop B:

sudo ip a a 192.168.50.1/16 dev enp2s0f0

wobei „enp2s0f0“ euer Netzwerkinterface ist.

Auf Laptop A:

sudo ip a a 192.168.50.2/24 dev enp2s0
sudo ip r a 192.168.50.0/24 via 192.168.50.1 dev enp2s0

Danach sollten beide Geräte sich pingen können.

Als nächstes gibt man in diesem Netzwerk die Festplatte von A frei:

nbd-server -d -C /dev/null 1337 /dev/sda

Parameter:

  • -d : Debugmodus, Prozess geht nicht in den Hintergrund
  • -C /dev/null: Keine Konfigurationsdatei laden
  • 1337: Port, auf dem die Freigabe läuft
  • /dev/sda: Festplatte, die freigegeben werden soll

Auf B bindet man das Gerät entsprechend ein:

nbd-client -nofork 192.168.50.2 1337 /dev/nbd0

Parameter:

  • -nofork: Nicht in den Hintergrund gehen
  • 192.168.50.2: IP von A
  • 1337: Port auf dem A die Freigabe hat
  • /dev/nbd0: 1. Freigabe von A.

Als nächstes braucht man ein wenig VirtualBox-Magic, um VirtualBox die Netzwerkfreigabe als eigenständige Festplatte vorzugaukeln:

VBoxManage internalcommands createrawvmdk -filename /tmp/laptop-windows.vmdk -rawdisk /dev/nbd0 

Parameter:

  • -filename: Pfad zur neuen vmdk-Datei („Symlink“)
  • -rawdisk: Pfad zur echten Festplatte, nämlich der Netzwerkfreigabe, also der Festplatte auf A

Nun kann man in VirtualBox eine neue virtuelle Maschine anlegen. Dabei gibt man als „vorhandene Festplatte“ den Pfad „/tmp/laptop-window.vmdk“ an. Alle Änderungen die man in der virtuellen Maschine vornimmt, werden dann über das Netzwerk auf die Festplatte von Laptop A geschrieben.

Problem gelöst und Windows endlich installiert :)

~ Sebastian

]]>
https://technik.blogbasis.net/windows-ohne-usb-stick-cd-laufwerk-installieren-20-05-2016/feed 0
Mit der AMD R9 290 Scrypt-Coins minen -> 800kh/s https://technik.blogbasis.net/mit-der-amd-r9-290-scrypt-coins-minen-800khs-03-01-2014 https://technik.blogbasis.net/mit-der-amd-r9-290-scrypt-coins-minen-800khs-03-01-2014#respond Fri, 03 Jan 2014 19:44:29 +0000 http://technik.blogbasis.net/?p=992 Nach langem Zögern habe ich mich entschieden leicht in das Mining einzusteigen. Da für SHA-basierte Coins der Zug schon abgefahren ist, habe ich mich für Scrypt-Coins entschieden (bsp. Litecoin, Dogecoin, usw.).

Im folgenden werde ich beschreiben, wie man sich ein Mining „Rig“ aufbaut, das auf > 800kh/s kommt.

Die Hardware

An sich benötigt man keine besonder Hardware, bis auf eine potente Grafikkarte, sowie ein passendes Netzteil.

  • „einfaches“ Mainboard
  • „einfache“ CPU – Single Core reicht aus
  • „etwas“ RAM – 512 MB bis 2 GB RAM
  • „kleine“ Festplatte – Nur zum Installieren vom Betriebssystem (USB-Stick reicht eigentlich auch)
  • potente Grafikkarte – Nur die bringt ordentlich KH/s
  • gutes Netzteil – 500W sollten mindestens drin sein – Je nach Grafikkarte
  • Gehäuse (optional) – Im Zweifel liegt das MB auf dem Schreibtisch
  • Strommessgerät (optional) – So kann man den Stromverbrauch im Auge behalten

Sehr angenehm ist es, wenn man für die Konfiguration noch einen Bildschirm, eine Maus und eine Tastatur zur Hand hat.

Windows + Cgminer

Für das Minen mit Grafikkarten braucht man einen funktionierenden Treiber. Da Grafikkartentreiber für Windows ausgereifter als die für Linux sind, viel die Wahl diesmal auf Windows. Ich hatte Windows 8 zur Hand, jedoch sollte jedes modernere Windows auch die Arbeit erledigen.

Folgende Schritte sind zunächst durchzuführen:

  • Hardware verdrahten
  • Windows installieren
  • Alle dazugehörigen Treiber (Grafikkarte!) installieren und nach Bedarf konfigurieren

Danach lädt man sich das folgende Mining-Package herunter: http://www.xup.in/dl,82934806/MiningPackage.rar/

Darin ist die Software namens „cgminer.exe“ in Version 3.7.2 enthalten. Diese führt die Berechnungen des Scrypt-Algorithmus auf der Grafikkarte aus. Das Archiv entpackt man sich einfach auf den Desktop und löscht den Ordner „Mining with CPU“.

AMD R9 290 und Cgminer.exe konfigurieren

Nach dem ich meine AMD R9 290 ausgepackt und im „Rig“ eingebaut hatte, hat es mich erstmal ungefähr eine Woche Zeit gekostet, die perfekten Einstellungen zu finden.

Man wechselt zunächst ins „Mining with GPU“/AMD Verzeichnis und legt dort eine Datei namens „start.bat“ mit folgendem Inhalt an:

@echo off
set GPU_USE_SYNC_OBJECTS 1
set GPU_MAX_ALLOC_PERCENT 100
setx GPU_USE_SYNC_OBJECTS 1
setx GPU_MAX_ALLOC_PERCENT 100
cgminer.exe -c litecoin.conf   

Später wird zum Starten des Miningvorgangs nur die „start.bat“ ausgeführt. Diese setzt erstmal ein paar Umgebungsvariablen und führt danach „cgminer.exe“ mit der litecoin.conf-Konfiguration aus.

Die Datei „litecoin.conf“ legen wir als nächstes an:

{
"pools" : [
	{
		"url" : "stratum+tcp://pooledbits.com:8338",
		"user" : "gehaxelt.graka",
		"pass" : "x"
	}
],
"intensity" : "20",
"vectors" : "1",
"worksize" : "256",
"lookup-gap" : "2",
"thread-concurrency" : "28000",
"gpu-engine" : "1000",
"auto-fan" : true,
"gpu-memclock" : "1350",
"gpu-powertune" : "20",
"temp-cutoff" : "100",
"temp-overheat" : "95",
"temp-target" : "85",

"api-port" : "4028",
"expiry" : "120",
"failover-only" : true,
"gpu-threads" : "1",

"log" : "5",
"queue" : "1",
"scan-time" : "60",
"temp-hysteresis" : "3",

"scrypt" : true,
"kernel" : "scrypt"
}

Im oberen Abschnitt konfiguriert man den Mining-Pool, in dem man mitrechnen möchte. Ich werde hier nicht alle Einstellungen im Details erklären. Die wichtigsten Parameter sind folgende:

  • Intensity: 1-20
  • Thread-concurrency 1-32000?
  • worksize: 256 – 512
  • temp-target: 85°C
  • temp-overheat: 95°C
  • temp-cutoff: 100°C

An diesen Stellschrauben kann man drehen, um noch ein paar Kh/s mehr aus der Grafikkarte zu kitzeln. Die letzten drei Parameter sind selbsterklärend.

Die oben gezeigte Konfiguration liefert mir bei einer Stromaufnahme von 380W 830kh/s. Die Temperatur bleibt dabei bei warmen 85°C.

Beim Experimentieren mit den Einstellungen kann der PC auch mal neustarten bzw. sich aufhängen. Das ist nicht weiter schlimm.

Autostart einrichten

Damit man keine Zeit verliert, falls der PC sich mal neustartet, packt man eine Verknüpfung der „start.bat“ in den Autostartordner. Das klappt folgendermaßen:

  • Explorer öffnen und „%AppData%“ in die Pfadleiste schreiben
  • Weiter nach „Microsoft\Windows\Start Menu\Programs\Startup“ navigieren
  • Im zweiten Explorer wieder in den Ordner mit der „start.bat“ navigieren
  • Mit „gedrücktem“ Rechtsklick die „start.bat“ in den Startup-Ordner ziehen
  • In dem gezeigten Menü „Verknüpfung erstellen“ auswählen
  • Fertig

Nun sollte der Mining-Prozess mit dem Start von Windows fortgesetzt werden.

Teamviewer zur Fernsteuerung

Ich möchte nicht ganze Zeit meinen Bildschirm an dem Mining-Rechner stecken haben. Daher wird einfach Teamviewer installiert und so konfiguriert, dass ein festes Passwort verwendet werden kann.

Selbst wenn der PC ohne Bildschirm startet, so hat man mit Teamviewer Zugriff auf den Rechner, um ggf. weitere Konfigurationen vorzunehmen.

Was sollte man minen?

Je nach Risikobereitschaft bzw. Strompreis muss man schauen, in welche Coins man Rechenzeit investiert. Um einen groben Überblick zu bekommen gibt es die Seite CoinWarz.com.

Fazit

Mining ist ein teures Hobby. Am Ende wird man wahrscheinlich nur Verlust machen, sodass man auf einer utopischen Stromrechnung + Hardwarekosten sitzen bleibt. Andererseits könnte es sein, dass in einigen Monaten ein Coin stark an Wert gewinnt, und man die Kosten gedeckt bekommt.

Nicht zu vergessen ist auch die Lärmbelästigung, denn es hört sich dann 24/7 so an, als würde jemand Staubsaugen.

~Sebastian

]]>
https://technik.blogbasis.net/mit-der-amd-r9-290-scrypt-coins-minen-800khs-03-01-2014/feed 0
WinAPI Crypting https://technik.blogbasis.net/winapi-crypting-08-10-2013 https://technik.blogbasis.net/winapi-crypting-08-10-2013#respond Tue, 08 Oct 2013 13:32:39 +0000 http://technik.blogbasis.net/?p=555 Vorwort

Dieser etwas kleinere Artikel dreht sich um das Thema WinAPI-Crypting.
Warum sollte man sich damit überhaupt befassen?
API-Crypting ist immer dann hilfreich, wenn man möchte, dass erst während der Laufzeit bekannt wird, welche Funktionen aufgerufen werden. Zum Beispiel wenn man das Leben eines Reversers erschweren möchte oder ähnliches.

Was steckt hinter dem API-Crypting

Umgang mit Dlls

Unter Windows haben wir die Möglichkeit eine beliebige Dll-Datei zu laden und alle Funktionen, die diese Dll anbietet, aufzurufen. Dabei müssen wir nur den Namen der Dll und den Funktionsnamen kennen.
Das ganze arbeitet dann mit den drei Funktionen LoadLibrary, GetProcAddress und FreeLibrary. LoadLibrary lädt eine beliebige Dll-Datei in den virtuellen Speicher des aufrufenden Prozesses und gibt dabei ein Handle zu dieser Dll zurück. GetProcAddress durchsucht dann die Export-Tabelle, also die Tabelle in der alle Funktionen stehen, die außerhalb des Moduls angesprochen werden können, nach unserer Funktion und gibt die Addresse zurück. FreeLibrary entlädt die Dll aus dem aufrufenden Prozess.

HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
FARPROC WINAPI GetProcAddress(HMODULE hModule,LPCSTR lpProcName);
BOOL WINAPI FreeLibrary(HMODULE hModule);

Funktionszeiger

C++ ermölicht es Objekte über ihre Addresse anzusprechen. Doch warum sollte man dies nur auf Objekte beschränken. Durch type-defines können wir sogenannte Funktionszeiger definieren und diese auf eine Addresse im speicher setzen.
Anschließend können wir diese Funktionszeiger wie normale Funktionen aufrufen.

typedef int (WINAPI * MessageBoxPtr)(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
MessageBoxPtr Message = Addresse;
Message(0,"Durch Funktionszeiger aufgerufen","Information",0);

Das Vorgehen beim WinAPI-Crypting

Zunächst benötigen wir folgendes
– den Namen der Funktion in der Export-Tabelle
– den Namen der Dll-Datei in der die Funktion enthalten ist.
– einen Funktionszeigern, den wir auf die Addresse im Speicher setzen können

Der Funktionsname als auch der Name der Dll-Datei werden als verschlüsselte Strings im SRC gespeichert.
Beim Ausführen des Programms werden die Strings entschlüsselt, die Dll in den Prozesspeicher geladen und ein Funktionszeiger auf die Addresse der Funktion gesetzt. Zuletzt kann man den Funktionszeiger wie gewohnt aufrufen.

Ein kleines Beispiel

Angenommen wir möchten folgenden SRC mithilfe von WinAPI-Crypting verschlüsseln

#include <iostream>
using namespace std;

int main(int argc, char** argv)
{
    MessageBoxA(0,"Normaler Funktionsaufruf","Information",0);
    return 0;
}

Der einzige WinAPI-Aufruf den wir haben ist der Aufruf von MessageBoxA und durch die MSDN erfahren wir, dass die Funktion in der Dll „User32.dll“ als Ascii-Funktion (MessageBoxA) und als Unicode(Widechar)-Funktion (MessageBoxW) enthalten ist. Also schnell einen Funktionszeiger gebastelt, die Dll per LoadLibraryA laden lassen und die Funktionsaddresse per GetProcAddress() erhalten. Ich hab in dem folgenden Code schon den Namen der Dll und den Namen der Funktion in einem std::string gespeichert

#include <iostream>
#include <string>
using namespace std;

typedef int (WINAPI * MessageBoxPtr)(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
std::string Dll = "User32.dll";
std::string Function = "MessageBoxA";

int main(int argc, char** argv)
{
    MessageBoxPtr Message = (MessageBoxPtr) GetProcAddress(LoadLibraryA(Dll.c_str()),Function.c_str());
    if(Message != 0)
	Message(0,"Durch Funktionszeiger aufgerufen","Information",0);

    return 0;
}

Was bleibt noch über? Naja es heißt ja „WinAPI-Crypting„, also sollten wir auch den Funktionsnamen und den Namen der Dll verschlüsseln und zur Laufzeit übersetzen. Da ich hier nicht näher auf verschiedene Verschlüsselungsalgorithmen eingehen möchte, verschiebe ich einfach jeden Buchstaben um eine Stelle in der Ascii-Tabelle. So erhalte ich für „User32.dll“ „Vtfs43/emm“ und für „MessageBoxA“ „NfttbhfCpyB“.

Der gesamte Code sieht nun wie folgt aus

#include <iostream>
#include <string>
using namespace std;

typedef int (WINAPI * MessageBoxPtr)(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);
std::string Dll = "Vtfs43/emm";
std::string Function = "NfttbhfCpyB";

void decrypt_strings();

int main(int argc, char** argv)
{
    decrypt_strings();
    MessageBoxPtr Message = (MessageBoxPtr) GetProcAddress(LoadLibraryA(Dll.c_str()),Function.c_str());
    Message(0,"Durch Funktionszeiger aufgerufen","Information",0);
    return 0;
}

void decrypt_strings()
{
    unsigned int i;
    for(i=0;i<Dll.length();i++)
    {
        Dll[i] = (char) (((int) Dll[i])-1);
    }

    for(i=0;i<Function.length();i++)
    {
        Function[i] = (char) (((int) Function[i])-1);
    }
}

Abschließende Worte

Ich hoffe ihr hattet, trotz des schlechten Themas, Spaß beim Lesen und der ein oder andere Leser hat etwas neues erfahren.

mfg
TgZero

]]>
https://technik.blogbasis.net/winapi-crypting-08-10-2013/feed 0
Daten an eine ausführbare Datei hängen https://technik.blogbasis.net/daten-an-eine-ausfuehre-datei-haengen-04-05-2013 https://technik.blogbasis.net/daten-an-eine-ausfuehre-datei-haengen-04-05-2013#respond Sat, 04 May 2013 19:44:25 +0000 http://technik.blogbasis.net/?p=414 Vorwort

In manchen Fällen möchte man Daten, die erst nach der Entwicklung des Programms vorhanden sind, dem Programm hinzufügen. (In diesem Artikel wird nur das PE32-Format behandelt!) Nun wäre es möglich das ganze Projekt neu zu kompilieren mit den Daten. Doch wie geht der Entwickler vor, wenn nur der Endbenutzer die Daten kennt und diese eintragen möchte? Sollte der Entwickler den SRC mitliefern oder muss der Endbenutzer dem Entwickler bei jeder Änderung der Daten um ein neuens Programm bitten?

Das Builder-Stub Prinzip

Die Lösung für unser Problem nennt sich auch das „Builder-Stub“-Prinzip. Dabei haben wir zwei unterschiedliche Programme. Den Builder und das Stub-Programm, wobei der Builder unsere Daten an das Stub-Programm anhängt und der Stub die Daten ausliest. Wobei wir eine Kopie der Stub erstellen und diese dann verändern. So hat man für jede Datenanhängung eine ausführbare Datei und das Stub-Programm bleibt in seinem ursprünglichen Zustand.

Bisher kenne ich drei unterschiedliche Implementierungen des Builder-Stub-Prinzips:
– EOF
– Ressource
– Manipulation der PE-Sektion

EOF

EOF steht (wie viele wohl schon vermutet haben) für End Of File. Für unseren Builder heißt dies, dass dieser die Daten an das Ende der Stub schreibt. Beim Auslesen muss man nur die genaue Byte-Anzahl kennen, oder die Anzahl vom Builder direkt nach den Daten anhängen. Somit ist es möglich Informationen unterschiedlicher Größe an eine Stub anzuhängen. Im Folgenden Beispiel habe ich mich für eine feste Anzahl an Bytes entschieden, allerdings kann man dies mit ein wenig Fantasie ändern. Außerdem wurde eine Signatur hinzugefügt, damit das Ausführen des Stub-Programms (also ohne Daten) reibungslos funktioniert.


bool WriteDataToStubEOF(const char* Data, size_t Size,std::string Signatur = "TG",std::string StubPath ="",std::string OutPath ="")
{
std::fstream writer;

if(StubPath == "" | OutPath == "")
{
	char Filepath[256];
	std::string Path;
	GetModuleFileNameA(0,Filepath,255);
	Path = Filepath;
	Path = Path.substr(0,Path.find_last_of('\')+1); //'
	StubPath = Path + "Stub.exe";
	OutPath = Path + "Output.exe";
}

if(CopyFileA(StubPath.c_str(),OutPath.c_str(),false))
{
	writer.open(OutPath.c_str(),std::ios_base::out|std::ios_base::binary|std::ios_base::app);
	if(writer.good())
	{
		writer.write(Data,Size);
		writer.write(reinterpret_cast<char*> (&Size),sizeof(size_t));
	writer.write(Signatur.c_str(),Signatur.length());
	writer.close();
	return true;
	}
	else
	{
		MessageBoxA(0,"Output-Datei konnte leider nicht geoeffnet und beschrieben werden","Datei-Fehler",0);
	}
	writer.close();
	return false;
}
else
{
	MessageBoxA(0,"Stub konnte nicht kopiert werden","Kopier-Fehler",0);
}
}

char* ReadDataFromStubEOF(size_t& Size,std::string Signatur = "TG")
{
char* Data;
std::fstream reader;
char Filepath[256];
GetModuleFileNameA(0,Filepath,255);

reader.open(Filepath,std::ios_base::in|std::ios_base::binary);
if(reader.good())
{
	char* Temp;
	size_t Len;
	size_t Offset;
	Temp = new char[Signatur.length()];

	reader.seekg(0,std::ios_base::end);
	Len = reader.tellg();
	reader.seekg(0,std::ios_base::beg);
	Offset = Len - Signatur.length();
	reader.seekg(Offset);
	reader.read(Temp,Signatur.length());

	if(strncmp(Signatur.c_str(),Temp,Signatur.length()) == 0)
	{
		Offset -= sizeof(size_t);
		reader.seekg(Offset);
		reader.read((char*) &Size,sizeof(size_t));
		Offset -= Size;
		reader.seekg(Offset);

		Data = new char[Size];
		reader.read(Data,Size);
		return Data;
	}
	else
		MessageBoxA(0,"Signatur stimmte nicht ueberein","Signatur-Fehler",0);

	delete[] Temp;
	}
	else
		MessageBoxA(0,"Konnte Prozessdatei nicht oeffnen","Datei-Fehler",0);

reader.close();
return 0;
}

Der SRC sollte von jedem verstanden werden. Nun muss man die Funktionen lediglich in dem Builder oder in dem Stub-Programm aufrufen. Dabei müssen natürlich die Signaturen übereinstimmen. Falls noch Fragen diesbezüglich offen sind kann die Person mir eine Mail schreiben, oder ein Kommentar verfassen.

Ressourcen

Eine ausführbare Datei unter Windows, also im PE-Format besitzt sogenannte Ressourcen.
Diese Ressourcen sind Bytes die durch eine Addresse oder durch einen eindeutigen Namen aufgelöst werden können. Dabei unterscheidet man noch zwischen diversen Ressourcen-Typen. Fast jede ausführbare Datei hat ein Icon, welches vom Windows-Explorer angezeigt wird. Genau dieses Icon wird in der Datei als Ressource gespeichert mit dem Typ „RT_ICON“. Dank der WinAPI ist es kinderleicht mit Ressourcen einer PE-Datei zu arbeiten. Außerdem brauchen wir nun keine Signatur mehr, da die Ressource als Signatur verwendet werden kann.

Bearbeiten der Ressourcen


HANDLE WINAPI BeginUpdateResource(LPCSTR pFileName,BOOL bDeleteExistingResources);

pFileName ist der Pfad zu unserer Datei als C-String und bDeleteExistingResources sollte wohl klar sein (true fürs Löschen!).
Die Funktion liefert uns einen Zeiger zu der geladenen Datei. Diesen Zeiger brauchen wir für die weiteren Funktionen.


BOOL WINAPI UpdateResource(HANDLE hUpdate,LPCSTR lpType,LPCSTR lpName,WORD wLanguage,LPVOID lpData,DWORD cbData);

hUpdate ist der Zeiger unserer Datei, den wir zuvor per BeginUpdateResource erhalten haben.
lpType und lpName identifizieren die Ressource. Wobei man anstatt einem C-String auch die Funktion MAKEINTRESOURCEA() aufrufen kann, welche ein WORD erwartet.
wLanguage bezeichnet die zu verwendende Sprache (nicht weiter behandelt).
lpData ist ein Zeiger zu unseren Daten, die wir anhängen möchten und cbData entspricht der Größe der Daten die wir als Ressource speichern möchten.
Falls lpData 0 ist, so wird die Ressource gelöscht.


BOOL WINAPI EndUpdateResource(HANDLE hUpdate,BOOL fDiscard);

hUpdate ist ein Zeiger zu unserer Datei und per fDiscard geben wir an, ob unsere Veränderungen verworfen oder beibehalten werden sollen.

Unser Builder macht nun folgendes:
Zu Beginn laden wir die Datei per „BeginUpdateResource()“ in den Speicher und erhalten einen Zeiger auf diese Datei, die wir für die weiteren API-Aufrufe benötigen. Per „UpdateResource()“ können wir nun eine neue Ressourcen hinzufügen, wobei wir einen konstanten Typ und Namen für die Ressource haben. Abschließend speichern wir die Datei und damit auch unsere Änderungen mit „EndUpdateResource()“.

Auslesen der Ressourcen

 

HRSRC WINAPI FindResource(HMODULE hModule,LPCTSTR lpName,LPCTSTR lpType);

hModule ist ein Zeiger zu unserer geladenen! Datei. Falls dieses 0 ist, so wird das Modul genommen, dessen Code-Segment gerade ausgeführt wird.
lpName und lpType dienen der Identifizierung der Ressource, wobei man alternativ auch MAKEINTRESOURCEA() benutzen kann.
Für unser Builder-Stub-Prinzip sollte man hier konstanten verwenden, die einmalig sind und im Build-Programm als auch im Stub-Programm gleich sind.
Der Rückgabewert wird für die weiteren Funktionen benötigt und ist ein Zeiger zu unserer gefundenen Ressource.

HGLOBAL WINAPI LoadResource(HMODULE hModule,HRSRC hResInfo);

hModule ist unser Modul (in unserem Beispiel 0) und hResInfo ist der Rückgabewert von FindResource.
Die Funktion liefert einen Zeiger zu den durch die Ressource assoziierten Daten.


LPVOID WINAPI LockResource(HGLOBAL hResData);

hResData ist der Rückgabewert von LoadResource und der Rückgabewert ist ein Zeiger zu den ersten Bytes der Ressource-Daten.


DWORD WINAPI SizeofResource(HMODULE hModule,HRSRC hResInfo);

hresInfo ist der Rückgabewert von FindResource und es wird ein DWORD mit der Größe unserer Ressource-Daten zurückgegeben.

Unserer Stub macht nun folgendes:
Zuerst erhält man per „FindResource()“ einen zeiger zu unserer Ressource-Eintrag, anschließend erhält man mithilfe der Funktion „LoadResource()“ einen Zeiger zu den assoziierten Daten der Ressource und per „LockResource()“ erhält man einen Zeiger zu den eigentlichen Ressource-Daten. Um die Größe der Ressource zu ermitteln benutzen wir die Funktion „SizeofResource()“. Anschließend alloziieren wir neuen Speicherplatz mit der Größe von SizeofResource() und kopieren dort die Daten hinein (falls wir schreibzugriff benötigen!)

Der Code


bool WriteDataToStubResource(const char* Data, size_t Size,WORD Name,WORD Type ,std::string StubPath="", std::string OutPath="")
{
if(StubPath == "" | OutPath == "")
{
	char Filepath[256];
	std::string Path;
	GetModuleFileNameA(0,Filepath,255);
	Path = Filepath;
	Path = Path.substr(0,Path.find_last_of('\')+1);
	StubPath = Path + "Stub.exe";
	OutPath = Path + "Output.exe";
}

if(CopyFileA(StubPath.c_str(),OutPath.c_str(),false))
{
	HANDLE	hFile;

	hFile = BeginUpdateResourceA(OutPath.c_str(),false);

	if(hFile != NULL)
	{
		if(UpdateResourceA(hFile,MAKEINTRESOURCEA(Type),MAKEINTRESOURCEA(Name),LANG_NEUTRAL,(void*) Data,(DWORD) Size) == true)
		{
			if(EndUpdateResourceA(hFile,false)==true)
				return true;
			else
				MessageBoxA(0,"Konnte Ressource nicht speichern","Ressource-Fehler",0);
		}
		else
			MessageBoxA(0,"Konnte Ressource nicht schreiben","Ressource-Fehler",0);
	}
	else
		MessageBoxA(0,"Konnte das Ressource-Handle nicht erzeugen","Handle-Fehler",0);
}
else
	MessageBoxA(0,"Konnte Stub nicht kopieren","Kopier-Fehler",0);

return false;
}

char* ReadDataFromStubResource(size_t& Size,WORD Name, WORD Type)
{
HRSRC		hResourceFound;
HGLOBAL		hResourceLoaded;
LPVOID		ResourceLocked;
DWORD		ResourceSize;
char*		Readed;

hResourceFound = FindResourceA(0,MAKEINTRESOURCEA(Name),MAKEINTRESOURCEA(Type));
if(hResourceFound != NULL)
{
	hResourceLoaded = LoadResource(0,hResourceFound);
	if(hResourceLoaded != NULL)
	{
		ResourceLocked = LockResource(hResourceLoaded);
		if(ResourceLocked != NULL)
		{
			ResourceSize = SizeofResource(0,hResourceFound);
			if(ResourceSize > 0)
			{
				Readed = new char[ResourceSize];
				Size = ResourceSize;
				memcpy(Readed,ResourceLocked,(size_t) ResourceSize);
				return Readed;
			}
			else
				MessageBoxA(0,"Groesse der Ressource ist nicht gueltig","Size-Fehler",0);
		}
		else
			MessageBoxA(0,"Konnte Resource nicht locken","Lock-Fehler",0);
	}
	else
		MessageBoxA(0,"Konnte Ressource nicht laden","Lade-Fehler",0);
}
else
	MessageBoxA(0,"Konnte Ressource nicht finden","Keine Ressource",0);

return 0;
}

Neue Sektion

Ich hatte kurz erwähnt, dass eine PE-Datei Ressourcen besitzt und somit auch einer festen Struktur folgt.
Befassen wir uns zunächst mit den Basiswissen über eine PE-Datei, denn dieses Wissen ist durchaus ausreichend für unser Vorhaben.

Beim Ausführen einer PE-Datei legt Windows einen neuen Prozess an und alloziiert Speicher im virtuellen Speicher.
Anschließend wird die Datei an einer festen Addresse geladen, der sogenannten ImageBase. Eine PE-Datei besitzt sogenannte Sektionen. Diese Sektionen entsprechen einem Bereich im virtuellen Speicher in dem bestimmte Daten stehen. Zum Beispiel gibt es eine Sektion für die Code-Instruktionen, meistens auch TEXT-, oder CODE-Sektion genannt. Der Grund wieso man eine PE-Datei in Sektionen unterteilt ist denkbar einfach und lautet Zugriffsrechte. Warum sollte man im Bereich der Code-Sektion Daten schreiben wollen, oder warum sollte man den Bereich in dem die Ressourcen gespeichert werden als auführbar makieren?
Wichtig ist hierbei zu beachten, dass die Sektionen immer ein vielfaches von dem SectionAlignment (im Speicher) und vom FileAlignment (auf der Festplatte) sein müssen.
Doch woher soll Windows wissen, ab wann eine Sektion im PE-Format beginnt?

Aufbau einer PE-Datei

Zunächst einmal beginnt eine PE-Datei mit dem IMAGE_DOS_HEADER. Durch IMAGE_DOS_HEADER.e_lfanew gelanden wir zum nächsten Image dem IMAGE_NT_HEADERS. Und nach diesem IMAGE_NT_HEADERS folgt dann für jede Sektion ein IMAGE_SECTION_HEADER. Da die Größe der Header,also alles bis zur ersten Sektion, auch ein vielfaches sein muss, finden sich nach dem letzten IMAGE_SECTION_HEADER viele Nullbytes bevor die erste Sektion beginnt. Genau dort werden wir unseren neuen Sektionsheader einfügen und unsere Sektion ans Ende der Datei schreiben.
Der IMAGE_NT_HEADERS hat eine Signatur und zwei weitere Header, den IMAGE_OPTIONAL_HEADER und den IMAGE_FILE_HEADER. Beide Header liefern uns weitere Informationen über den Aufbau der ausführbaren Datei. Nach den ganzen Sektionen, inklusive der Nullbytes, finden wir die eigentlichen Sektionen, also die jeweiligen Daten der Sektionen.

Werfen wir nun einen Blick auf den IMAGE_NT_HEADERS, damit wir auch eine gültige Sektion zur Datei hinzufügen, wobei wir nur die Werte betrachten, die für unser Vorhaben wichtig sind.

IMAGE_NT_HEADERS

IMAGE_OPTIONAL_HEADER.ImageBase
Ist die Addresse an der unsere Datei von Windows geladen wird.

IMAGE_OPTIONAL_HEADER.SectionAlignment und IMAGE_OPTIONAL_HEADER.FileAlignment
Unsere Sektionsgröße muss ein vielfaches dieser beiden Werte sein, falls nicht müssen wir ein paar Nullbytes hinzufügen.

IMAGE_OPTIONAL_HEADER.SizeOfImage
Ist die Größe des Speichers den Windows alloziieren muss, damit die Datei in diesen geladen werden kann.

IMAGE_OPTIONAL_HEADER.SizeOfHeaders
Die Größe aller Header zusammen.

IMAGE_FILE_HEADER.NumberOfSections
Anzahl der Sektionen innerhalb der Datei.

IMAGE_SECTION_HEADER

Name
Der 8-Byte-Lange Name der Sektion (z.B. .text)

VirtualSize
Die Größe der Sektion, wenn diese in dem Speicher geladen wird.

VirtualAddress
Addresse an der unsere Sektion geladen wird. Hierbei ist die Addresse relativ zur ImageBase. Wenn wir eine ImageBase von 0x40000 haben und eine virtuelle Addresse von 0x01234 befindet sich unsere Sektion an der Addresse 0x41234.

SizeOfRawData
Größe der Sektion wenn wir sie von der Festplatte einlesen. Dieser Wert ist größer oder gleich der VirtualSize.
Durch das Alignment haben wir oft Nullbytes in der Sektion auf der Festplatte, die von Windows nicht beachtet werden, wenn die Datei in den Speicher geladen wird.

PointerToRawData
Offset zum ersten Byte der Sektions-Daten.

Characteristics
Legt unter anderem die Zugriffsrechte der Sektion fest (executable, readable, writeable…)

Eine Sektion hinzufügen

Da wir nun alle Werte kennen die wir fürs Hinzufügen benötigen, sollten wir uns den genauen Ablauf verdeutlichen.
Eine Überprüfung der Datei ist nicht unbedingt nötig, wäre aber für den allgemeinen gebrauch durchaus sinnvoll.

– Datei in ein Array einlesen
– e_lfanew auslesen um zu den NT_HEADERS zu kommen
– Überprüfen ob nach dem letzten Section-Header genügend Nullbytes vorhanden sind
– Sämtliche Section-Header durchgehen um die nächste virtuelle Addresse zu ermitteln.
– einen neuen Section-Header erstellen und diesen mit Werten füllen
– NumberOfSections erhöhen
– SizeOfImage erhöhen
– den neuen Section-Header nach dem letzten Section-Header einfügen
– Sektions-Daten ans Ende der Datei schreiben.

Das Auslesen der Sektion

Ich hoffe der ein oder andere wird an dieser Stelle laut aufschreien „ImageBase“ und genau das ist der Wert den wir auch brauchen. Diesen bekommen wir mithilfe der Funktion GetModuleHandleA(NULL). Anschließend müssen wir uns nur durch die Sektions-Struktur hangeln und dabei überprüfen, ob wir bei unserer Sektion angelangt sind.
Anschließend können wir mit ImageBase + IMAGE_SECTION_HEADER.VirtualAddress und IMAGE_SECTION_HEADER.VirtualSize die Sektion einlesen.

Der Code

Das ganze sieht nun wie folgt aus:


DWORD Align(DWORD Value, DWORD Alignment)
{
DWORD Out;
Out = ((Value % Alignment)== 0) ? Value : (Value + (Alignment - (Value % Alignment)));
return Out;
}

bool WriteDataToStubSection(const char* Data, size_t Size,const char* Name,std::string StubPath="", std::string OutPath="")
{
std::fstream		Reader;
std::fstream		Writer;
char*			File;
size_t			FileSize;

DWORD			OffsetNewHeader;	
IMAGE_DOS_HEADER*	DosHeader;
IMAGE_NT_HEADERS32*	NtHeaders;
IMAGE_SECTION_HEADER*	SectionHeader;
IMAGE_SECTION_HEADER	NewSection;

if(StubPath == "" | OutPath == "")
{
	char Filepath[256];
	std::string Path;
	GetModuleFileNameA(0,Filepath,255);
	Path = Filepath;
	Path = Path.substr(0,Path.find_last_of('\')+1); //'
	StubPath = Path + "Stub.exe";
	OutPath = Path + "Output.exe";
}

memset((void*) &NewSection,0x00,sizeof(IMAGE_SECTION_HEADER));
// Datei vollstaendig per fstream einlesen und in File speichern
Reader.open(StubPath.c_str(),std::ios_base::binary|std::ios_base::in);
if(Reader.good())
{
		Reader.seekg(0,std::ios_base::end);
		FileSize = Reader.tellg();
		File = new char[FileSize];
		Reader.seekg(0,std::ios_base::beg);
		Reader.read(File,FileSize);
		Reader.close();

		DosHeader = (IMAGE_DOS_HEADER*) File;
		NtHeaders = (IMAGE_NT_HEADERS32*) &File[DosHeader->e_lfanew];

		OffsetNewHeader = (DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + NtHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
		// Ueberpruefe, ob nach dem letzten Header genug Platz fuer einen neuen ist
		// NewSection wurde vorher komplett auf 0x00 gesetzt.
		if(memcmp((void*) &File[OffsetNewHeader],(void*) &NewSection,sizeof(NewSection)) == 0)
		{
			//Genug Platz fuer neuen Section-Header
			// Naechstgroesste Virtuelle Addresse berechnen
			for(unsigned int i=0;i<NtHeaders->FileHeader.NumberOfSections;i++)
			{
				SectionHeader = (IMAGE_SECTION_HEADER*) &File[DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + i*sizeof(IMAGE_SECTION_HEADER)];
				if(NewSection.VirtualAddress <= (SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize))
				{
					NewSection.VirtualAddress = Align(SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize,NtHeaders->OptionalHeader.SectionAlignment);
				}
			}
			// Informationen ueber die Sektion dem neuen header uebermitteln
			// Benutze hierfuer die Hilfsfunktion Align, welche die Werte zu einem naechst hoeheren Vielfachen einer Zahl rundet.
			strncpy((char*) &NewSection.Name,Name,8);
			NewSection.Misc.VirtualSize = Size;
			NewSection.NumberOfLinenumbers = 0;
			NewSection.NumberOfRelocations = 0;
			NewSection.PointerToRelocations = 0;
			NewSection.Characteristics = 0x40000000 |IMAGE_SCN_MEM_SHARED; // READABLE!
			NewSection.PointerToRawData = FileSize;
			NewSection.SizeOfRawData = Align(Size,NtHeaders->OptionalHeader.FileAlignment);
			memcpy(&File[OffsetNewHeader],&NewSection,sizeof(IMAGE_SECTION_HEADER));

			//Setze neue Informationen im Nt-Header
			NtHeaders->FileHeader.NumberOfSections++;
			NtHeaders->OptionalHeader.CheckSum = 0;
			NtHeaders->OptionalHeader.SizeOfImage = Align(NewSection.VirtualAddress + NewSection.Misc.VirtualSize,NtHeaders->OptionalHeader.SectionAlignment);
			//NtHeaders->OptionalHeader.SizeOfHeaders = ?

			// Datei speichern und Sektion hinten anhaengen
			// Wegen des Alignments noch ein paar 0x00 anhaengen!
			Writer.open(OutPath.c_str(),std::ios_base::out|std::ios_base::binary);
			if(Writer.good())
			{
				Writer.write(File,FileSize);
				Writer.write(Data,Size);
				for(unsigned long i=0;i<NewSection.SizeOfRawData - Size;i++)
				{
					Writer.put(0x00);
				}
				Writer.close();
				Reader.close();
				return true;
			}
		}
}
if(Writer.is_open())
	Writer.close();

if(Reader.is_open())
	Reader.close();

return false;
}

char* ReadDataFromStubSection(const char* Name,size_t& Size)
{
char*			Data;
DWORD			ImageBase;
IMAGE_DOS_HEADER*	DosHeader;
IMAGE_NT_HEADERS32*	NtHeaders;
IMAGE_SECTION_HEADER*	SectionHeader;

ImageBase = (DWORD) GetModuleHandleA(0);
if(ImageBase != 0)
{
	DosHeader = reinterpret_cast<IMAGE_DOS_HEADER*> (ImageBase);
	NtHeaders = reinterpret_cast<IMAGE_NT_HEADERS32*> (ImageBase + DosHeader->e_lfanew);

	SectionHeader = 0;
	for(unsigned int i=0;i<NtHeaders->FileHeader.NumberOfSections;i++)
	{
		IMAGE_SECTION_HEADER* Section;
		Section = reinterpret_cast<IMAGE_SECTION_HEADER*> (ImageBase + DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS32) + i* sizeof(IMAGE_SECTION_HEADER));
		if(strncmp(Name,(const char*) &Section->Name,8) == 0)
			SectionHeader = Section;
	}
	if(SectionHeader != 0)
	{
		Size = SectionHeader->Misc.VirtualSize;
		Data = new char[Size];
		memcpy(Data,(void*) (ImageBase + SectionHeader->VirtualAddress),Size);
		return Data;
	}
	else
		MessageBoxA(0,"Sektion wurde nicht gefunden","Keine Sektion",0);
}
else
	MessageBoxA(0,"Konnte ImageBase nicht berechnen","Ungueltiges Handle",0);

return 0;
}

Abschließende Worte

Dieser Artikel war schon etwas lang und ein wenig komplizierter. Ich hoffe es hat euch genauso viel Spaß gemacht diesen Artikel zu lesen, wie mir ihn zu schreiben. Und seid nett zum lieben Gehaxelt, er durfte das ganze hier Probelesen. Falls noch Fragen offen sind, besonders bezüglich des PE-Formats wie immer ein Kommentar verfassen oder mir eine Mail schreiben. Ich weiß längst nicht alles über das PE-Format, werde mir dennoch Mühe geben alle zu beantworten.
Da ich diverse Probleme mit dem Einbinden des Quelltextes hatte, stelle ich diese als Archiv zur Verfügung, um lästiges Kopieren zu vermeiden.
DatenAnAusfuehrbareDateiSRC
Und den ganzen Artikel gibt es natürlich auch als PDF.

mit freundlichen Grüßen
euer TgZero

]]>
https://technik.blogbasis.net/daten-an-eine-ausfuehre-datei-haengen-04-05-2013/feed 0