
Un outil de traçage des API Windows en ligne de commande pour les binaires Golang.
Un outil de traçage de l'API Windows en ligne de commande pour les binaires Golang.
Note : Cet outil est une POC (preuve de concept) et un prototype en cours de développement, veuillez le traiter comme tel. Les retours sont toujours les bienvenus !
Bien que les programmes Golang comportent de nombreuses nuances quant à leur construction et leur comportement à l'exécution, ils doivent toujours interagir avec la couche OS, ce qui signifie qu'à un moment donné, ils doivent appeler des fonctions de l'API Windows.
Le package runtime de Go contient une fonction appelée asmstdcall et cette fonction est une sorte de « passerelle » utilisée pour interagir avec l'API Windows. Puisque cette fonction est censée appeler les fonctions de l'API Windows, nous pouvons supposer qu'elle a besoin d'accéder à des informations telles que l'adresse de la fonction et ses paramètres, et c'est là que les choses deviennent intéressantes.
Asmstdcall reçoit un seul paramètre qui est un pointeur vers une structure similaire à la suivante :
struct LIBCALL {
DWORD_PTR Addr;
DWORD Argc;
DWORD_PTR Argv;
DWORD_PTR ReturnValue;
[...]
}
Certains de ces champs sont remplis après l'appel de la fonction API, comme la valeur de retour, d'autres sont reçus par asmstdcall, comme l'adresse de la fonction, le nombre d'arguments et la liste des arguments. Quoi qu'il en soit, il est clair que la fonction asmstdcall manipule beaucoup d'informations intéressantes concernant l'exécution de programmes compilés en Golang.
gftrace exploite asmstdcall et son fonctionnement pour surveiller des champs spécifiques de la structure mentionnée et les enregistrer pour l'utilisateur. L'outil est capable d'enregistrer le nom de la fonction, ses paramètres ainsi que la valeur de retour de chaque fonction Windows appelée par une application Golang. Le tout sans avoir besoin de hooker une seule fonction API ou d'en avoir une signature.
L'outil essaie également d'ignorer tout le bruit de l'initialisation du runtime Go et n'enregistre que les fonctions appelées après celle-ci (c'est-à-dire les fonctions du package main).
Si vous voulez en savoir plus sur ce projet et cette recherche, consultez le billet de blog.
Téléchargez la dernière version.
gftrace.exe <filepath> <params>
Tout ce que vous avez à faire est de spécifier les fonctions que vous souhaitez tracer dans le fichier gftrace.cfg, en les séparant par une virgule sans espaces :
CreateFileW,ReadFile,CreateProcessW
Les fonctions exactes de l'API Windows qu'une méthode Golang X d'un package Y appellerait dans un scénario spécifique ne peuvent être déterminées que par l'analyse de la méthode elle-même ou en essayant de les deviner. Il existe certaines caractéristiques intéressantes qui peuvent être utilisées pour les déterminer, par exemple, les applications Golang semblent toujours préférer appeler les fonctions des ensembles « Wide » et « Ex » (par exemple CreateFileW, CreateProcessW, GetComputerNameExW, etc.) vous pouvez donc en tenir compte lors de votre analyse.
Le fichier de configuration par défaut contient plusieurs fonctions que j'ai déjà testées (du moins la plupart) et je peux affirmer avec certitude qu'elles peuvent être appelées par une application Golang à un moment donné. J'essaierai de le mettre à jour éventuellement.
Traçage de CreateFileW() et ReadFile() dans un fichier Golang simple qui appelle "os.ReadFile" deux fois :
- CreateFileW("C:\Users\user\Desktop\doc.txt", 0x80000000, 0x3, 0x0, 0x3, 0x1, 0x0) = 0x168 (360)
- ReadFile(0x168, 0xc000108000, 0x200, 0xc000075d64, 0x0) = 0x1 (1)
- CreateFileW("C:\Users\user\Desktop\doc2.txt", 0x80000000, 0x3, 0x0, 0x3, 0x1, 0x0) = 0x168 (360)
- ReadFile(0x168, 0xc000108200, 0x200, 0xc000075d64, 0x0) = 0x1 (1)
Traçage de CreateProcessW() dans le malware TunnelFish :
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000ace98, 0xc0000acd68) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000c4ec8, 0xc0000c4d98) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc00005eec8, 0xc00005ed98) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000bce98, 0xc0000bcd68) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\system32\cmd.exe", "cmd /c "wmic computersystem get domain"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000c4ef0, 0xc0000c4dc0) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\system32\cmd.exe", "cmd /c "wmic computersystem get domain"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000acec0, 0xc0000acd90) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\system32\cmd.exe", "cmd /c "wmic computersystem get domain"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000bcec0, 0xc0000bcd90) = 0x1 (1)
[...]
Traçage de plusieurs fonctions dans le malware Sunshuttle :
- CreateFileW("config.dat.tmp", 0x80000000, 0x3, 0x0, 0x3, 0x1, 0x0) = 0xffffffffffffffff (-1)
- CreateFileW("config.dat.tmp", 0xc0000000, 0x3, 0x0, 0x2, 0x80, 0x0) = 0x198 (408)
- CreateFileW("config.dat.tmp", 0xc0000000, 0x3, 0x0, 0x3, 0x80, 0x0) = 0x1a4 (420)
- WriteFile(0x1a4, 0xc000112780, 0xeb, 0xc0000c79d4, 0x0) = 0x1 (1)
- GetAddrInfoW("reyweb.com", 0x0, 0xc000031f18, 0xc000031e88) = 0x0 (0)
- WSASocketW(0x2, 0x1, 0x0, 0x0, 0x0, 0x81) = 0x1f0 (496)
- WSASend(0x1f0, 0xc00004f038, 0x1, 0xc00004f020, 0x0, 0xc00004eff0, 0x0) = 0x0 (0)
- WSARecv(0x1f0, 0xc00004ef60, 0x1, 0xc00004ef48, 0xc00004efd0, 0xc00004ef18, 0x0) = 0xffffffff (-1)
- GetAddrInfoW("reyweb.com", 0x0, 0xc000031f18, 0xc000031e88) = 0x0 (0)
- WSASocketW(0x2, 0x1, 0x0, 0x0, 0x0, 0x81) = 0x200 (512)
- WSASend(0x200, 0xc00004f2b8, 0x1, 0xc00004f2a0, 0x0, 0xc00004f270, 0x0) = 0x0 (0)
- WSARecv(0x200, 0xc00004f1e0, 0x1, 0xc00004f1c8, 0xc00004f250, 0xc00004f198, 0x0) = 0xffffffff (-1)
[...]
Traçage de plusieurs fonctions dans l'agent du framework DeimosC2 :
- WSASocketW(0x2, 0x1, 0x0, 0x0, 0x0, 0x81) = 0x130 (304)
- setsockopt(0x130, 0xffff, 0x20, 0xc0000b7838, 0x4) = 0xffffffff (-1)
- socket(0x2, 0x1, 0x6) = 0x138 (312)
- WSAIoctl(0x138, 0xc8000006, 0xaf0870, 0x10, 0xb38730, 0x8, 0xc0000b746c, 0x0, 0x0) = 0x0 (0)
- GetModuleFileNameW(0x0, "C:\Users\user\Desktop\samples\deimos.exe", 0x400) = 0x2f (47)
- GetUserProfileDirectoryW(0x140, "C:\Users\user", 0xc0000b7a08) = 0x1 (1)
- LookupAccountSidw(0x0, 0xc00000e250, "user", 0xc0000b796c, "DESKTOP-TEST", 0xc0000b7970, 0xc0000b79f0) = 0x1 (1)
- NetUserGetInfo("DESKTOP-TEST", "user", 0xa, 0xc0000b7930) = 0x0 (0)
- GetComputerNameExW(0x5, "DESKTOP-TEST", 0xc0000b7b78) = 0x1 (1)
- GetAdaptersAddresses(0x0, 0x10, 0x0, 0xc000120000, 0xc0000b79d0) = 0x0 (0)
- CreateToolhelp32Snapshot(0x2, 0x0) = 0x1b8 (440)
- GetCurrentProcessId() = 0x2584 (9604)
- GetCurrentDirectoryW(0x12c, "C:\Users\user\AppData\Local\Programs\retoolkit\bin") = 0x39 (57)
[...]
gftrace est publié sous la licence GPL v3. Veuillez vous référer au fichier nommé LICENSE pour plus d'informations.