Sudami KillMe
juillet 15th, 2008 at 06:04 admin
Toujours en quête de nouvelles techniques de rootkit pour Windows, je suis tombé en lisant mes RSS sur un POC chinois réalisé par Sudami qui permet de rendre un process immortel. Intéressé par la chose j’ai voulu prendre le code source mais manque de bol d’après le google translate il faut être inscrit et faire partie d’une communauté underground aux yeux bridés pour y avoir accès. Cependant on peut download le binaire librement. Après quelques heures de reverse je vous propose de plonger au coeur de ce nouveau bébé très innovant qui permet de bien faire chier n’importe quel utilisateur et AV. Le must pour un rootkit :]
Dans le .rar, on ne trouve qu’un simple binaire nommé « sudami.exe » qui fait 67,5 Ko, dès le départ on se doute qu’il va devoir charger un driver dans le noyau au vu du titre du post « DKOM to Protect EXE self,without any hook », DKOM signifiant Direct Kernel Object Manipulation on sait que le programme va opérer au niveau noyau. Le binaire est packé avec PECompact 2.x, pour l’unpack rien de très difficile, on trace le programme de SEH en SEH pour tomber sur celui qui saute sur l’OEP (Original Entry Point). Pour info ce saut est en 0x42BF8A (jmp eax). Une fois qu’on est sur l’OEP on peut tranquillement dumper le binaire avec OllyDump ou LordPE+Imprec pour retrouver le binaire d’origine. L’orignal pèse 180 Ko, un coup d’oeil aux ressources avec LordPe nous permet de voir une ressource nommée « SYS » qui, en regardant le dump hexa, commence avec « MZ » (0x4D 0x5A 0×90), ca nous suffit largement pour dire que cette ressource est un binaire, après extraction ce binaire fait 9 Ko. Le champ Subsystem du PE header du binaire extrait vaut 1, ce qui veut dire que celui-ci est un driver. En regardant le plus près le binaire celui-ci va chargé le driver en utilisant le Service Manager avec les APIs OpenSCManager, OpenService et ControlService puis va spawner une window avec plein de mots chinois qu’on comprend pas dedans. Il va ensuite envoyer une série d’IOCTL au driver (0×80002000, 0×80002008, 0×80002010 et 0×80002018) avec l’API DeviceIoControl pour rendre le process immortel. Le mieux est que vous regardiez par vous même le binaire sous IDA ou sous Olly pour voir son fonctionnement. Bref au final on se retrouve avec une popup en chinois qui reste ouverte sans qu’on puisse la killer. Tout cela n’est pas la partie visible de l’iceberg, le vrai stuff intéressant se trouve bien évidemment dans le driver.
Commence donc l’étude du driver, premièrement je le charge en VM histoire de regarder ce qu’il définit comme MajorFunctions et ses Devices :
0: kd> !drvobj \driver\sudami 3 Driver object (815c87e0) is for: *** ERROR: Module load completed but symbols could not be loaded for sudami.sys \Driver\sudami Driver Extension List: (id , addr) Device Object list: 813b1490 DriverEntry: f9e08005 sudami DriverStartIo: 00000000 DriverUnload: f9e04250 sudami AddDevice: 00000000 Dispatch routines: [00] IRP_MJ_CREATE f9e042a0 sudami+0x12a0 [01] IRP_MJ_CREATE_NAMED_PIPE 804f9709 nt!IopInvalidDeviceRequest [02] IRP_MJ_CLOSE f9e042a0 sudami+0x12a0 [03] IRP_MJ_READ 804f9709 nt!IopInvalidDeviceRequest [04] IRP_MJ_WRITE 804f9709 nt!IopInvalidDeviceRequest [05] IRP_MJ_QUERY_INFORMATION 804f9709 nt!IopInvalidDeviceRequest [06] IRP_MJ_SET_INFORMATION 804f9709 nt!IopInvalidDeviceRequest [07] IRP_MJ_QUERY_EA 804f9709 nt!IopInvalidDeviceRequest [08] IRP_MJ_SET_EA 804f9709 nt!IopInvalidDeviceRequest [09] IRP_MJ_FLUSH_BUFFERS 804f9709 nt!IopInvalidDeviceRequest [0a] IRP_MJ_QUERY_VOLUME_INFORMATION 804f9709 nt!IopInvalidDeviceRequest [0b] IRP_MJ_SET_VOLUME_INFORMATION 804f9709 nt!IopInvalidDeviceRequest [0c] IRP_MJ_DIRECTORY_CONTROL 804f9709 nt!IopInvalidDeviceRequest [0d] IRP_MJ_FILE_SYSTEM_CONTROL 804f9709 nt!IopInvalidDeviceRequest [0e] IRP_MJ_DEVICE_CONTROL f9e043e0 sudami+0x13e0 [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL 804f9709 nt!IopInvalidDeviceRequest [10] IRP_MJ_SHUTDOWN 804f9709 nt!IopInvalidDeviceRequest [11] IRP_MJ_LOCK_CONTROL 804f9709 nt!IopInvalidDeviceRequest [12] IRP_MJ_CLEANUP 804f9709 nt!IopInvalidDeviceRequest [13] IRP_MJ_CREATE_MAILSLOT 804f9709 nt!IopInvalidDeviceRequest [14] IRP_MJ_QUERY_SECURITY 804f9709 nt!IopInvalidDeviceRequest [15] IRP_MJ_SET_SECURITY 804f9709 nt!IopInvalidDeviceRequest [16] IRP_MJ_POWER 804f9709 nt!IopInvalidDeviceRequest [17] IRP_MJ_SYSTEM_CONTROL 804f9709 nt!IopInvalidDeviceRequest [18] IRP_MJ_DEVICE_CHANGE 804f9709 nt!IopInvalidDeviceRequest [19] IRP_MJ_QUERY_QUOTA 804f9709 nt!IopInvalidDeviceRequest [1a] IRP_MJ_SET_QUOTA 804f9709 nt!IopInvalidDeviceRequest [1b] IRP_MJ_PNP 804f9709 nt!IopInvalidDeviceRequest 0: kd> !devobj 813b1490 Device object (813b1490) is for: devsudami \Driver\sudami DriverObject 815c87e0 Current Irp 00000000 RefCount 0 Type 00000022 Flags 00000040 Dacl e12a719c DevExt 00000000 DevObjExt 813b1548 ExtensionFlags (0000000000) Device queue is not busy. 0: kd> !object \Global?? Object: e1002898 Type: (817ef418) Directory ObjectHeader: e1002880 (old version) HandleCount: 1 PointerCount: 126 Directory Object: e1001be0 Name: GLOBAL?? Hash Address Type Name ---- ------- ---- ---- 00 e147efe0 SymbolicLink D: e13173c8 SymbolicLink NDIS e138fbd0 SymbolicLink DISPLAY1 [...................................] 31 e17b5fe0 SymbolicLink sudami [...................................]
Le driver va donc créer un device nommé sudami avec un SymbolicLink (qui est donc mit dans l’ObjectDirectory \Global??) autorisant la communication depuis le user-land qui s’appelle aussi sudami. On retrouve seulement 2 MajorFunctions, la même pour les IRP_MJ_CREATE et IRP_MJ_CLOSE qui sert juste à complêter l’IRP avec un STATUS_SUCCESS.
0: kd> uf f9e042a0 sudami+0x12a0: f9e042a0 8bff mov edi,edi f9e042a2 55 push ebp f9e042a3 8bec mov ebp,esp f9e042a5 8b450c mov eax,dword ptr [ebp+0Ch] ; IRP f9e042a8 c7401800000000 mov dword ptr [eax+18h],0 ; IRP.IoStatus.Status=0=STATUS_SUCCESS f9e042af 8b4d0c mov ecx,dword ptr [ebp+0Ch] ; IRP f9e042b2 c7411c00000000 mov dword ptr [ecx+1Ch],0 ; IRP.IoStatus.Information=0 f9e042b9 32d2 xor dl,dl f9e042bb 8b4d0c mov ecx,dword ptr [ebp+0Ch] f9e042be ff151860e0f9 call dword ptr [sudami+0x3018 (f9e06018)] (IofCompleteRequest) f9e042c4 33c0 xor eax,eax f9e042c6 5d pop ebp f9e042c7 c20800 ret 8
Enfin on a la MajorFunction qui gère les IOCTL en 0xf9e043e0. Maintenant il est temps de sortir IDA pour analyser la routine DispatchDeviceControl.
Première chose qui saute aux yeux et nous fait pissé du sang, le code du driver est offusqué, il contient plein de junk code posé dans les fonctions et IDA n’apprécie pas du tout pour l’analyse. Juste pour déconner voici la gueule du DriverEntry du driver :
.text:000110B0 .text:000110B0 ; Attributes: bp-based frame .text:000110B0 .text:000110B0 sub_110B0 proc near .text:000110B0 .text:000110B0 var_18 = dword ptr -18h .text:000110B0 .text:000110B0 8B FF mov edi, edi .text:000110B2 55 push ebp .text:000110B3 8B EC mov ebp, esp .text:000110B5 83 EC 18 sub esp, 18h .text:000110B8 C7 45 E8 00 00+ mov [ebp+var_18], 0 .text:000110BF .text:000110BF loc_110BF: .text:000110BF 8D 05 C5 10 01+ lea eax, loc_110C5 .text:000110C5 .text:000110C5 loc_110C5: .text:000110C5 83 C0 0E add eax, 0Eh .text:000110C8 EB 05 jmp short loc_110CF .text:000110CA ; --------------------------------------------------------------------------- .text:000110CA EB F3 jmp short loc_110BF .text:000110CA ; --------------------------------------------------------------------------- .text:000110CC DB 30 57 db 0DBh, 30h, 57h .text:000110CF ; --------------------------------------------------------------------------- .text:000110CF .text:000110CF loc_110CF: .text:000110CF FF E0 jmp eax .text:000110CF sub_110B0 endp .text:000110CF .text:000110CF ; --------------------------------------------------------------------------- .text:000110D1 E9 21 74 db 0E9h, 21h, 74h .text:000110D4 03 75 01 E8 8B+ dd 0E8017503h, 0C708458Bh .text:000110DC ; --------------------------------------------------------------------------- .text:000110DC 40 inc eax
On retrouve ce schéma de junk dans quasiment toutes les fonctions du driver, l’auteur s’est dit qu’il y aurait bien des gars capables de reverser son driver pour piquer sa technique donc il ajouter du junk dans le code à la main en insérant des macros toutes les 2 lignes de code C pour rendre la tâche du reverser plus longue mais pas impossible ! En effet, en regardant de plus près on a un « lea eax, loc_110C5″ suivit d’un « add eax, 0EH », eax vaut donc loc_110C5+0EH. Ensuite nous avons un « jmp short loc_110CF » qui saute sur un « jmp eax », nous devons donc normalement arriver en loc_110C5+0EH et la IDA n’arrive pas analyser correctement, c’est l’échec :] En 0x110D3 (0x110C5+0xE) on peut voir l’opcode 0×74 qui correspond à un JZ et en 0x110D5 on peut voir l’opcode d’un JNZ (0×75), le plus drôle c’est que le JZ saute 3 bytes plus loin et le JNZ 1 byte plus loin, donc tous les deux sautent au même endroit, c’est à dire en 0x110D8. Hop on nettoie ca sous IDA pour obtenir le code clarifié :
.text:000110B8 C7 45 E8 00 00+ mov dword ptr [ebp-18h], 0 .text:000110BF 8D 05 C5 10 01+ lea eax, loc_110C5 .text:000110C5 .text:000110C5 loc_110C5: .text:000110C5 83 C0 0E add eax, 0Eh .text:000110C8 EB 05 jmp short loc_110CF .text:000110C8 ; --------------------------------------------------------------------------- .text:000110CA EB db 0EBh ; Ù .text:000110CB F3 db 0F3h ; ¾ .text:000110CC DB db 0DBh ; ¦ .text:000110CD 30 db 30h ; 0 .text:000110CE 57 db 57h ; W .text:000110CF ; --------------------------------------------------------------------------- .text:000110CF .text:000110CF loc_110CF: .text:000110CF FF E0 jmp eax .text:000110CF ; --------------------------------------------------------------------------- .text:000110D1 E9 db 0E9h .text:000110D2 21 db 21h ; ! .text:000110D3 ; --------------------------------------------------------------------------- .text:000110D3 74 03 jz short loc_110D8 .text:000110D5 75 01 jnz short loc_110D8 .text:000110D5 sub_110B0 endp .text:000110D5 .text:000110D5 ; --------------------------------------------------------------------------- .text:000110D7 E8 db 0E8h ; Þ .text:000110D8 ; --------------------------------------------------------------------------- .text:000110D8 .text:000110D8 loc_110D8: .text:000110D8 .text:000110D8 8B 45 08 mov eax, [ebp+arg_0] .text:000110DB C7 40 38 A0 12+ mov dword ptr [eax+38h], offset sub_112A0 .text:000110E2 8D 05 E8 10 01+ lea eax, loc_110E8
Le junk consiste donc à un jmp sur 2 jumps conditionnels qui ensemble forment le même saut, jump if zero (JZ) qui saute si le ZeroFlag de l’EFlags est à 1 et jump if not zero (JNZ) qui fait l’inverse. On peut donc remplacer tout ce code par des NOP vu qu’il est inutile et regrouper les NOPs dans un tableau pour obtenir un disass clair. Après avoir fait le ménage on obtient :
.text:000110B8 C7 45 E8 00 00+ mov dword ptr [ebp-18h], 0 .text:000110B8 00 00 ; --------------------------------------------------------------------------- .text:000110BF 90 90 90 90 90+ db 19h dup(90h) .text:000110D8 ; --------------------------------------------------------------------------- .text:000110D8 8B 45 08 mov eax, [ebp+8]
Ce qui est clairement plus lisible ! Il ne reste plus qu’a automatiser la tâche avec un script IDC. Pour le script j’ai fait simple, j’ai recherché tout les « jmp eax » dans le binaire avec la function FindBinary, j’ai noppé les instructions autour qui ne servaient à rien puis regrouper les NOPs dans un tableau. Voici le script très simple :
// // Cleaner for driver http://hi.baidu.com/sudami/blog/item/1fe5b203005f45e909fa9368.html // Ivanlef0u // #include <idc.idc> static main() { auto i; auto addr; Message("Starting clean script ...\n"); addr=0; do { // // jmp eax= 0xFF 0xE0 // addr=FindBinary(addr, SEARCH_DOWN, "FF E0"); Message("jmp eax at : 0x%x\n", addr); // // 16 bytes avant le "jmp eax" commence le junk et se termine 9 bytes plus loin // for (i=0; i<16+9; i++) PatchByte(addr-16+i, 0x90); // // Instrution suivante // addr=addr+2; }while(addr!=(-1+2)); //((addr=-1 lorsque plus rien) addr=0; // // Met les nop dans des tableaux // do { addr=FindBinary(addr, SEARCH_DOWN, "90"); Message("nop at : 0x%x\n", addr); for(i=0; i<25; i++) { MakeUnknown(addr+i, 1, DOUNK_SIMPLE); } MakeArray(addr, 25); // // Instrution suivante // addr=addr+26; }while(addr!=(-1+26)); //((addr=-1 lorsque plus rien) }
Une fois qu'on a nettoyé le driver on peut enfin commencer à analyser son fonctionnement, nous allons juste nous intéresser à la fonction DispatchDeviceControl. On sait que le driver sert à rendre le process immortel, il va donc devoir agir sur la structure EPROCESS et très certainement sur les structures ETHREADs correspondant aux threads du process. Pour analyser la routine de dispatch des IOCTLs nous allons juste suivre les fonctions appelées en fonction des IOCTLs passé au driver.
Commençons par le premier IOCTL (0x80002000) celui-ci nous va utliser IoGetCurrentProcess pour récupérer l'EPROCESS du process courant, donc celle du programme à protéger et à partir du pointeur sur la double liste chainée ThreadListHead de la KPROCESS (offset KPROCESS+0x50) va parcourir la liste de structures ETHREAD représentant les threads du process pour changer le champ KernelApcDisable (offset 0xD4 de la KTRHEAD), si ce champ est à 0 alors il le passe à la valeur 0xA98AC7 (grut?!). En regardant les symbols on peut voir que ce champ fait 2 bytes, alors d'après ce que j'ai compris ce champ agirait comme un compteur. Je m’explique lorsqu'on envoie un APC (Asynchronous Procedure Call) à un thread 3 routines sont mises en place, une KernelRoutine qui est appelée directement en préemptant le thread en kernel-land à un IRQL de APC_LEVEL, une NormalRoutine qui est généralement appelée en user-land lorsque le thread est en state Waiting puis une RundownRoutine qui est la routine qui sera appelée lorsque le thread sera killé et qui à pour rôle de faire le ménage sur les APC qui n'auront pas été exécutés. La fonction KiInsertQueueApc va vérifier la valeur du champ KernelApcDisable si celui-ci est à 0 alors la fonction queue un APC kernel pour le thread. Lorsque le thread appel une des APIs KeEnterCriticalRegion ou KeEnterGuardedRegion le champ KernelApcDisable est décrémenté inversement pour les APIS KeLeaveCriticalRegion et KeLeaveGuardedRegion, ce qui faut retenir c'est que la valeur de KernelApcDisable est différente de 0 dès que le thread rentre dans une région critique. Du fait qu'il est possible d'avoir des plusieurs appels à KeEnterCriticalRegione et KeEnterGuardedRegion un compteur est mis en place afin de connaître le nombre d'appels à ces APIs, après je n'arrive pas à comprendre pourquoi ce dernier compte à l'envers ... Je reviendrais sur rôle des APCs kernel plus loin dans ce post, d'abord voyons les actions des autres IOCTLs.
Continuons avec le second IOCTL envoyé au driver (0x80002008), la fonction associée va aussi parcourir la liste des threads du process mais cette fois ci pour modifier le champ State (offset 0x2D de la KTHREAD) à la valeur 4 qui correspond à l'état Terminated, celui la même qui est définit lorsqu'on appel TerminateThread. Juste pour rappel voici les différents états possibles pour un thread sous Windows :
// // Thread scheduling states. // typedef enum _KTHREAD_STATE { Initialized, Ready, Running, Standby, Terminated, //=4 Waiting, Transition, DeferredReady, GateWait } KTHREAD_STATE;
Le troisième IOCTL (0x80002010) est beaucoup plus freestyle, la fonction chargée de gérer cet IOCTL va commencer par récupérer l'adresse de la fonction PsTerminateSystemThread et scanner ses instructions une par une en utilisant un LDE (Length Disassembly Engine) qui permet d'obtenir la taille des instructions en fonction de leurs opcodes, ca se voit sous IDA vu la bouillie que crée la fonction LDE. La fonction de scan recherche l'instruction commençant par (0x80F6) puis lit le word situé 2 bytes plus loin par rapport au début de l'instruction. Pour savoir à quoi ça correspond il suffit de désassembler la fonction PsTerminateSystemThread :
lkd> uf PsTerminateSystemThread nt!PsTerminateSystemThread: 805d2c2a 8bff mov edi,edi 805d2c2c 55 push ebp 805d2c2d 8bec mov ebp,esp 805d2c2f 64a124010000 mov eax,dword ptr fs:[00000124h] 805d2c35 f6804802000010 test byte ptr [eax+248h],10h 805d2c3c 7507 jne nt!PsTerminateSystemThread+0x1b (805d2c45) nt!PsTerminateSystemThread+0x14: 805d2c3e b80d0000c0 mov eax,0C000000Dh 805d2c43 eb09 jmp nt!PsTerminateSystemThread+0x24 (805d2c4e) nt!PsTerminateSystemThread+0x1b: 805d2c45 ff7508 push dword ptr [ebp+8] 805d2c48 50 push eax 805d2c49 e828fcffff call nt!PspTerminateThreadByPointer (805d2876) nt!PsTerminateSystemThread+0x24: 805d2c4e 5d pop ebp 805d2c4f c20400 ret 4
L'instruction qui commence par 0x80F6 est "test byte ptr [eax+248h],10h", 2 bytes après le début des opcodes on retrouve la valeur 0x0248 qui tout l'offset d'un champ dans la structure ETHREAD. L'auteur à tout simplement implémenter une méthode générique pour retrouver l'offset de ce champ en se basant sur le code de la fonction noyau PsTerminateSystemThread, c'est pas mal, je dirais même bien mais je ne pense pas que ca soit nécessaire, un coup de PsGetVersion avec un switch pour définir les offsets suffit aussi et à l'avantage d'être beaucoup moins lourd. Quoiqu'il en soit à l'offset 0x248 de la structure on retrouve le champ CrossThreadFlags, un byte servant à définir différents flags pour le thread :
lkd> dt nt!_ETHREAD [...] +0x248 CrossThreadFlags : Uint4B +0x248 Terminated : Pos 0, 1 Bit +0x248 DeadThread : Pos 1, 1 Bit +0x248 HideFromDebugger : Pos 2, 1 Bit +0x248 ActiveImpersonationInfo : Pos 3, 1 Bit +0x248 SystemThread : Pos 4, 1 Bit +0x248 HardErrorsAreDisabled : Pos 5, 1 Bit +0x248 BreakOnTermination : Pos 6, 1 Bit +0x248 SkipCreationMsg : Pos 7, 1 Bit +0x248 SkipTerminationMsg : Pos 8, 1 Bit [...]
La fonction va parcourir la liste des threads du process et mettre le flag Terminated à 1 pour tous les threads.
Enfin le dernier IOCTL (0x80002018) va modifier le PID du process au niveau du champ UniqueProcessId à l'offset 0x84 de la structure EPROCESS à la valeur de 7 et le propager aux ETHREADS en modifiant le champ UniqueProcess de la structure CLIENT_ID située à l'offset 0x1Ec de la structure ETHREAD. Je suppose que cette modification est faite pour tromper la PspCidTable, cette table de handles qui correspond aux PID et qui permet de faire la translation PID->EPROCESS afin de tromper les APIs kernel qui normalement vérifient que les 2 valeurs de PID, celle envoyée et celle trouvée, correspondent, à vérifier quand même.
Ouf, finit le reverse d'IOCTLs, résumons à peu ce que nous avons 4 IOCTLS qui activent 4 protections différentes :
- Le premier sert à désactiver les APC kernel sur tout les threads du process.
- Le second va changer l'état du thread (State) à Terminated.
- Le troisième modifie les flags des différentes threads du process en activant le flag Terminated. La différence avec le point précédent c'est que le champ State sert uniquement au thread scheduler alors que le flag est utilisé par les APIs noyau pour vérifier l'état du thread.
- Enfin le dernier IOCTL change le PID du process et celui de ses threads à la valeur 7, une valeur qui n'existe pas dans la PspCidTable.
Je reviens sur la désactivation des APC noyau, le point qui me gênait le plus. En fait lorsqu'on appel l'API NtTerminateThread celle-ci appelle PspTerminateThreadByPointer après avoir référencé l'objet ETHREAD avec ObReferenceObjectByHandle et va envoyer un APC sur le thread avec la fonction KeInsertQueueApc. Remarquez que même la fonction PsTerminateSystemThread qui sert à killer un thread kernel en étant appelé par le thread lui même va appeler PspTerminateThreadByPointer. En fait en y pensant un peu cela est normal, un APC est une routine qui est exécutée dans le context d'un thread, il est normal qu'un thread ne puisse pas s'auto détruise, ca serait comme couper la branche sur laquelle il est, c'est donc une routine externe qui s'en charge. En l'occurrence cette routine s'appelle PsExitSpecialApc et lance PspExitThread. Donc comme le driver désactive les APC noyau en faisant croire que celui-ci est une région critique, l'APC chargé de killé le thread n'est pas lancé et ainsi le thread n'est pas détruit, tout s'explique ! (ou bien j’ai rien compris :p)
Pour conclure, je trouve le taff de Sudami vraiment très intéressant, ca innove et c'est basé sur des choses simples, on voit qu'il connaît très bien l'architecture du noyau Windows. En fait son code revient juste à faire croire au kernel que les threads du process sont déjà killés, le noyau ne va donc pas essayer de les détruire dans ce cas là et ils se verront toujours runner. La partie déobffuscation de code était aussi marrante, ce n’est pas très méchant, moi qui n'est pas vraiment l'habitude de voir des binaires comme ça j'ai apprit quelques trucs, cependant vivement IDA 5.3 qui permettra de faire du script avec des languages comme python ou ruby :]
Voilà, c'est fini, j'espère que vous avez appris pas mal de trucs, maintenant si quelqu'un est motivé pour recoder les fonctionnalités du driver, je suis preneur Je vous file une archive avec le binaire original, le binaire unpacké, le driver extrait et l'ouput html du reverse sous IDA de ce dernier. Vous trouverez tout ça ici : http://ivanlef0u.fr/repo/sudami_KillMe.rar
Enjoy !
Entry Filed under: RE
18 Comments
1. marc | juillet 15th, 2008 at 20:11
Même pour un ignare de mon calibre, cette escalade de subtile perversité entre l’auteur du code et le désassembleur est jubilatoire et époustouflante.
Ca vaut bien une tomme pour accompagner les mojito du Falstaff.
2. ollep | juillet 15th, 2008 at 21:53
Dommage qu’il y ait pas de easter egg pour killer la fenêtre quand même
3. Sha | juillet 16th, 2008 at 06:34
Ils sont quand même forts ces chinois :]
Superbe article dude, ça change de la musique trash et des picz gores héhé
4. Neitsa | juillet 16th, 2008 at 10:54
Salut Ivan
Chouette post et joli reverse ! Ha, ils sont forts ces chinois quand même…
Oui sous 2k3 (pas regardé pour Vista), mais pas sous XP et O.S précédents (où le champ fait 4 octets).
Les offsets sont différents aussi (0×70 sous 2k3, mais 0×40 sous XP), alors attention, il y a un gros risque de bugcheck sous un autre O.S que sous XP !
Si on voulait être « portable », il faudrait faire quelque chose comme ça – il me semble – pour changer la valeur de KernelDisableApc :
Parce que KiDeliverApc() vérifiera si le champ vaut 0 ou non (donc n’importe qu’elle autre valeur fait l’affaire si != 0). Si la valeur est != de 0 alors on ne délivre pas d’APC. Je vois pas d’autre raison…
Ceci dit, autant la méthode est chouette et bien pensée, autant je me demande ce qu’on faire avec un process planqué de la sorte (plus du tout d’APC délivrées, des threads à Terminated, un PID fantôme, etc.).
Le système laisse t’il un liberté d’action à un process et des threads dans un tel état (possible d’ouvrir un fichier par exemple ?) ou faut-il tout « réparer » , faire une action (ex. : ouverture de fichier) et revenir à un état « zombie » ?
5. admin | juillet 16th, 2008 at 11:02
@Marc
Tu peux me mailler ta tomme ?
@Ollep
Nan la DialogFunc de CreateDialogParam ne support pas les messages de type WM_DESTROY donc c’est mort
@Sha
Merci jeune folle.
@Neitsa
Oups erreur de ma part, le champ KernelApcDisable fait bien 4 bytes sous XP. Pour les APCs je ne suis pas d’accord avec ton code, la valeur max est tout simplement 1 ! Il ne pas oublier que KeLeaveCriticalRegion incrémente le compteur donc l’écart le plus grand avec 0 en incrémentant est égal à 0-0xFFFFFF=1 (en unsigned). Sinon le POC ne faisait aucune action après l’envoie des IOCTLs je ne sais pas comment se comporte l’OS avec un thread dans un tel état, pour être sur le mieux serait de tester.
6. ITI | juillet 16th, 2008 at 16:23
Coooool….. J’avais jamais lu un truc sympa comme ça, bien éclairci/traduit pour un noob du code comme moi (QB4, où est-tu ????) :o) merci.
A quand le kit de codage dans lequel on injecte les exploits et qui maintient les failles ouvertes tant qu’on a pas pu tuer ce super-loader ? :o] gruntz…
7. ollep | juillet 16th, 2008 at 16:51
@ITI, si on reloge le code dans un process « vitale » de windows, ça revient plus ou moins au même en pratique
Je vois qu’il y a des fans de gorilla.bas
8. CyberPunk | juillet 17th, 2008 at 01:11
Les PID sont des multis de 4, a ce que j’ai compris la le PID fait 7.
J’ai déja vu un POC qui permetait bypassé les FW en changent le PID de la sorte…
9. sudami | juillet 17th, 2008 at 05:07
Had inadvertently intruded into your treasure land, see this article
…
I was the author of this process, I am glad that you patronize my blog,
and the conduct of this procedure.
In fact, the method 3, KernelApcDidable, did not play a role in this
process.
there not too many tests and improved, so it can not compatible with
many platforms. Just run under XP SP2.
The most important point is: This procedure is released just for fun,
can not be used in the formal products, so the writing is very rough.
In addition, the author of this article analysis « drv.sys » very well,
but I had already share the source code http://www.debugman.com. add some jump
code in the drv.sys is juest for test,the source Code in fact is so
poor, so I’s not necessary to prevent others to reverse. Oh~~~
- –
French football,in this year’s European Cup, performance not well. ah,
the lack of a central figure such as Zidane; Henry is old …
and sad about that~
sorry for my pool English. ~
10. newsoft | juillet 18th, 2008 at 13:11
* Le pb de PsGetVersion() c’est que tu dois faire la table de switch toi-même … donc avoir toutes les versions de Windows et/ou les symboles sous la main …
* IDAPython et IDARuby ça existe déjà depuis un certain temps
* nibbles.bas forever
11. Taron | juillet 18th, 2008 at 15:21
Sympa ce killme…, c’est vrai qu’il utilise des trucs assez simples, ,et ça marche pas mal, beau reverse !
12. SSTA | juillet 20th, 2008 at 16:19
Une autre perversion du même genre signé par MJ une camarade de Sudami, posté sur son blog ( http://hi.baidu.com/mj0011/blog/item/90adc462293510d9e7113a11.html) et discuté en anglais sur les forums Sysinternals ( http://forum.sysinternals.com/forum_posts.asp?TID=14328 ).
D’autres codes intéressants sur Debugman/http://www.debugman.com/ (se familiariser avec un bon dico français/chinois est indispensable, et après quelques mois de pratique, il devient facile de naviguer sur n’importe quel site chinois. Si besoin est, les codes sont disponibles à toute personne intéressée); un peu moins sur Antiprotect (http://www.antiprotect.com/ ).
Bonne continuation.
13. sloshy | juillet 21st, 2008 at 17:01
Hello,
Une question me turlupine, en modifiant le processus de la manière que tu décris, celui n’est plus actif, si?
çàd qu’il executera une action (afficher une fenêtre à l’écran pour le cas présent) mais ne sera pas réactif.
Dans le cas d’une application concrète, notepad par exemple, il ne sera plus utilisable après l’avoir rendu « invincible » n’est ce pas? ce qui avouons le, limite le PoC en question (il me semble).
Ou peut être suis-je passé à coté d’une énormité?
14. admin | juillet 21st, 2008 at 17:06
@sloshy
Tu as ta réponse là http://0vercl0k.blogspot.com/2008/07/sudami-killme.html
15. sloshy | juillet 21st, 2008 at 19:00
merci, me voici donc avec un blog de plus à lire
16. Nameless | juillet 28th, 2008 at 15:33
C’est HS mais j’ai pas trouvé comment te contacter:
Une chose interessante pour detecter les process hidden :
http://forum.sysinternals.com/forum_posts.asp?TID=15457
a+
17. admin | juillet 28th, 2008 at 15:35
Merci Nameless, très intéressant j’avais déjà vu cette tech, pour me contact, ivanlef0u@security-labs.org.
18. none | décembre 15th, 2008 at 00:59
Juste pour info, l’outils Antirootkit GMER kill très bien sudami, sans broncher.
Trackback this post