Jste zde

Je skutečně FTDIChip-ID v FT232R unikátní?

Ve svém článku bych čtenáře rád seznámil se svými poznatky ohledně implementace identifikačních čísel FTDIChip-ID v obvodech FT232R společnosti FTDI a možnostech, jak tato unikátní čísla měnit, resp. jak klonovat obvody FT232R.

Společnost FTDI ve svém nejnovějším obvodu FT232R , určenému ke konverzi rozhraní USB a UART, implementovala na první pohled velmi příjemnou vlastnost, a to unikátní identifikační číslo, označované jako FTDIChip-ID. Taktéž nabízí dva projekty, FTDIChip-ID Projects a SafeGuard-IT Projects , s nimiž je možné identifikační čísla využívat v uživatelských aplikacích. Dokonce existuje verze obvodu v podobě USB klíče , sloužící pouze jako hardwarový bezpečnostní klíč. V katalogových listech a na webových stránkách vypadá identifikace obvodu velmi slibně a bezpečně, takže jistě nejeden vývojář začne uvažovat, zda-li tuto nabídnutou vlastnost nevyužije pro ochranu svých produktů proti nelegálnímu kopírování či klonování.

Kde se bere FTDIChip-ID

Jelikož se nikde nevyskytují podrobnější informace ohledně implementace identifikačního čísla, ze kterých by bylo možné posoudit, zda-li se skutečně jedná o věc bezpečnou a vhodnou k ochraně duševního vlastnictví, vrhnul jsem se na pole zpětného inženýrství. K analýze posloužila malá aplikace s jedním voláním funkce FTID_GetChipIDfromHandle() z knihovny FTChipID.dll. S debuggerem jsem snadno odhalil funkci identifikace FTDIChip-ID a možnosti, jak toto unikátní a neměnné číslo lehce změnit.

Součástí obvodu FT232R je i na čipu integrovaná paměť EEPROM obsahující základní nastavení obvodu (např. typ obvodu, USB VID a PID, USB sériové číslo, identifikace výrobce, popis produktu apod.) a také oblast vyhrazenou pro uživatelská data, ale to není vše. Na adresách 0x43 a 0x44 se nachází 32bitové sériové číslo čipu, které se zapisuje během jeho výroby a je neměnné. Čipy za sebou jdoucí v balící pásce mají po sobě jdoucí sériová čísla, v mém případě např. 0x70157ECA až 0x70157EDE. Samozřejmě by funkce FTID_GetChipIDfromHandle(), a jí podobné, mohly přímo vracet sériové číslo čipu, ale to by bylo asi příliš do očí bijící, takže se přečtené sériové číslo ještě zkrášluje. V přiloženém výpisu testovacího programu čtenář najde přesný postup výpočtu čísla FTDIChip-ID z přečteného sériového čísla čipu (funkce My_GetChipIDFromHandle() a BitShuffling()).

Pokud má čtenář k dispozici adaptér s obvodem FT232R, může si testovací aplikaci vyzkoušet přímo v praxi (přiložen spustitelný soubor FTChipID.exe). Před spuštěním je nutné nainstalovat ovladač verze 2.04.06 a doplnit knihovnu FTChipID.dll, která je k dispozici na webových stránkách společnosti FTDI.

Po spuštění programu z konzole se zobrazí následující výpis:

Number of FTDI devices is: 1
Dev 0:
 ID............. 4036001
 LocId.......... 22
 SerialNumber... 12R6UDE9
 Description.... USB-SP
 Driver version. 00040000
 Library version 00030112
 FTDIChip-ID.... FF2CFDD5
 FTDI Chip SN... 40117A33
 My__Chip-ID.... FF2CFDD5

Ve kterém jsou kromě základních informace o obvodu uvedené i položky: FTDI Chip SN (sériové číslo čipu), FTDIChip-ID (FTDIChip-ID přečtené funkcí FTID_GetChipIDfromHandle()) a My__Chip-ID (FTDIChip-ID přečtené vlastní funkcí My_GetChipIDFromHandle()). Je vidět, že obě přečtená čísla FTDIChip-ID se shodují.

Testovací aplikace pro čtení FTDIChip-ID

#include  
#include  
#include "ftd2xx.h" 
#include "FTChipID.h"  

BYTE BitShuffling (BYTE bSN)
   {
   return ((((((((bSN & 0x2) << 0x1) | (bSN & 0xF8)) << 0x3) | (bSN & 0x1)) << 1) |
             ((((((bSN >> 2) & 0x10) | (bSN & 0x87)) >> 0x1) | (bSN & 0x30)) >> 1)) & 0xff);
   }

FT_STATUS My_GetChipIDFromHandle
   (
   FT_HANDLE ftHandle,
   DWORD *MyChipID
   )
   
   {
   DWORD dwChipSerialNumber;
   WORD wTemp;

   if (FT_ReadEE (ftHandle, 0x43, &wTemp) != FT_OK) return (FT_EEPROM_READ_FAILED);
   dwChipSerialNumber = wTemp;
   if (FT_ReadEE (ftHandle, 0x44, &wTemp) != FT_OK) return (FT_EEPROM_READ_FAILED);

   dwChipSerialNumber |= wTemp << 16;
   printf (" FTDI Chip SN... %08X\n", dwChipSerialNumber);

   *MyChipID = ((BitShuffling (dwChipSerialNumber & 0xff) << 24) |
                          (BitShuffling ((dwChipSerialNumber >> 8) & 0xff) << 16) |
                          (BitShuffling ((dwChipSerialNumber >> 16) & 0xff) << 8) |
                          (BitShuffling ((dwChipSerialNumber >> 24) & 0xff) << 0)) ^ 0xA5F0F7D1;                
   return (FT_OK);
   }

void main
   (
   void
   )

     {
     FT_DEVICE_LIST_INFO_NODE *devInfo;
     FT_HANDLE ftHandle;
     DWORD numDevs, dwTemp, dwChipID;
     WORD i;

     if (FT_CreateDeviceInfoList (&numDevs) == FT_OK)
         {
         printf ("Number of FTDI devices is: %d\n",numDevs);
         devInfo = (FT_DEVICE_LIST_INFO_NODE*) malloc (sizeof(FT_DEVICE_LIST_INFO_NODE) * numDevs);

         if (FT_GetDeviceInfoList (devInfo, &numDevs) == FT_OK)
             {
             for (i = 0; i < numDevs; i++)
                 {
                 printf ("Dev %d:\n", i);
                 printf (" ID............. %x\n", devInfo[i].ID);
                 printf (" LocId.......... %x\n", devInfo[i].LocId);
                 printf (" SerialNumber... %s\n", devInfo[i].SerialNumber);
                 printf (" Description.... %s\n", devInfo[i].Description);

                 if (FT_OpenEx ((PVOID) devInfo[i].LocId, FT_OPEN_BY_LOCATION, &ftHandle) == FT_OK)
                    {
                    FT_GetDriverVersion (ftHandle, &dwTemp);
                    printf (" Driver version. %08X\n", dwTemp);
                    FT_GetLibraryVersion (&dwTemp);
                    printf (" Library version %08X\n", dwTemp);

                    /* tisk FTDIChip-ID pomoci funkce z knihovny FTChipID.dll */

                    if (FTID_GetChipIDFromHandle (ftHandle, &dwChipID) == FT_OK) printf (" FTDIChip-ID.... %08X\n", dwChipID);

                    /* tisk FTDIChip-ID pomoci primeho cteni obsahu pameti EEPROM */

                    if (My_GetChipIDFromHandle (ftHandle, &dwChipID) == FT_OK) printf (" My__Chip-ID.... %08X\n", dwChipID);

                    FT_Close (ftHandle);
                    }
                 else printf ("Error - FT_OpenEx()\n");
                 }
             }
         else printf ("Error - FT_GetDeviceInfoList()\n");
         free (devInfo);
         }
     else printf ("Error - FT_CreateDeviceInfoList()\n");
   }

Změna FTDIChip-ID

Změnit sériové číslo jeho přepsáním v interní paměti EEPROM není prakticky možné. Domnívám se, že sériové číslo se zapisuje do paměti v rámci finální kontroly funkce obvodu, kterou výrobce aktivuje vývodem TEST. Samozřejmě zjistit, jak se s obvodem pracuje v rámci jeho kontroly by bylo velmi obtížné, ne-li nemožné.

Druhá možnost tedy je, změnit přečtené sériové číslo v programové oblasti. V následujícím výpisu je symbolicky uveden běh volání funkce FTID_GetChipIDfromHandle() z uživatelské aplikace.

{
Aplikace
volani FTID_GetChipIDFromHandle() z FTChipID.dll
   {
   obsluha FTID_GetChipIDFromHandle() v FTChipID.dll
   volani FT_ReadEE (0x43) z ftd2xx.dll
       {
       obsluha FT_ReadEE() v ftd2xx.dll
       volani FT_IoCtl() z ftd2xx.dll
           {
           obsluha FT_IoCtl() v ftd2xx.dll
           volani DeviceIoControl z KERNEL32.dll
               {
               obsluha DeviceIoControl v KERNEL32.dll
               volani obsluhy funkce IoCtrl v ovladaci FT232R
               }
           }
        }
    volani FT_ReadEE (0x44) z ftd2xx.dll
      	{
        obsluha FT_ReadEE() v ftd2xx.dll
        volani FT_IoCtl() z ftd2xx.dll
            {
            obsluha FT_IoCtl() v ftd2xx.dll
            volani DeviceIoControl z KERNEL32.dll
               {
               obsluha DeviceIoControl v KERNEL32.dll
               volani obsluhy funkce IoCtrl v ovladaci FT232R
               }
            }
         }
    Prepocet serioveho cisla na FTDIChip-ID
   }

Možností, jak změnit již přečtené sériové číslo, se nabízí celá řada, přičemž jejich použitelnost je dána způsobem implementace v uživatelské aplikaci. Pro ilustraci pouze uvedu: úplná simulace funkce knihovny FTChipID.dll, zásah do kódu knihovny FTChipID.dll (knihovna vykazuje jisté známky ochrany proti úpravám), zásah do kódu knihoven ftd2xx.dll či KERNEL32.dll a v neposlední řadě úprava samotného ovladače.

Pro demonstraci lehkosti změny čteného sériového čísla čipu, resp. čísla FTDIChip-ID, jsem upravil knihovnu ftd2xx.dll. Z uvedeného výpisu je zřejmé, že stačí upravit obsluhu funkce čtení obsahu interní paměti EEPROM tak, že v případě čtení adres 0x43 a 0x44 se nevrací skutečně přečtený obsah, ale požadované číslo. V následujícím výpisu je uveden disasemblovaný kód obsluhy funkce FT_ReadEE. A v dalším výpisu je uvedena provedená změna kódu. Opět si čtenář může v praxi vyzkoušet funkci upravené knihovny. Programem FTD2XX_ModifChipID_hw.exe upravenou knihovnu ftd2xx.dll (určeno pro knihovnu z ovladače verze 2.04.06) stačí nakopírovat do adresáře s testovacím programem FTChipID.exe. Po spuštění se objeví již zmiňovaný výpis, ale s tím rozdílem, že čtený FTDIChip-ID bude vždy 0xDEDABABA. Činnost upravené knihovny si čtenář samozřejmě může vyzkoušet i se svými aplikacemi.

Výpis neupravené funkce FT_ReadEE

00202B50 FT_ReadEE             /$ 8B4C24 0C      MOV ECX,DWORD PTR SS:[ESP+C]
00202B54                       |. 6A 00          PUSH 0
00202B56                       |. 8D4424 10      LEA EAX,DWORD PTR SS:[ESP+10]
00202B5A                       |. 50             PUSH EAX
00202B5B                       |. 8B4424 0C      MOV EAX,DWORD PTR SS:[ESP+C]
00202B5F                       |. 6A 02          PUSH 2
00202B61                       |. 51             PUSH ECX
00202B62                       |. 6A 04          PUSH 4
00202B64                       |. 8D5424 1C      LEA EDX,DWORD PTR SS:[ESP+1C]
00202B68                       |. 52             PUSH EDX
00202B69                       |. 68 80002200    PUSH FTD2XX.00220080
00202B6E                       |. 50             PUSH EAX
00202B6F                       |. E8 ECFAFFFF    CALL FTD2XX.FT_IoCtl
00202B74                       \. C2 0C00        RETN 0C

Výpis upravené funkce FT_ReadEE (skutečná implementace je poněkud odlišná)

00202B44 <FT_ReadEE_Save>      /> 8B4C24 0C      MOV ECX,DWORD PTR SS:[ESP+C]
00202B48                       |. 51             PUSH ECX
00202B49                       |. EB 09          JMP SHORT <FTD2XX.FT_ReadEE_Cont> 
00202B4B                       |  90             NOP
00202B4C                       |  90             NOP
00202B4D                       |  CC             INT3
00202B4E                       |  CC             INT3
00202B4F                       |  CC             INT3
00202B50 FT_ReadEE             |$^EB F2          JMP SHORT <FTD2XX.FT_ReadEE_Save>
00202B52                       |  90             NOP
00202B53                       |  90             NOP
00202B54 <FT_ReadEE_Cont>      |> 6A 00          PUSH 0
00202B56                       |. 8D4424 14      LEA EAX,DWORD PTR SS:[ESP+14]
00202B5A                       |. 50             PUSH EAX
00202B5B                       |. 8B4424 10      MOV EAX,DWORD PTR SS:[ESP+10]
00202B5F                       |. 6A 02          PUSH 2
00202B61                       |. 51             PUSH ECX
00202B62                       |. 6A 04          PUSH 4
00202B64                       |. 8D5424 20      LEA EDX,DWORD PTR SS:[ESP+20]
00202B68                       |. 52             PUSH EDX
00202B69                       |. 68 80002200    PUSH FTD2XX.00220080
00202B6E                       |. 50             PUSH EAX
00202B6F                       |. E8 ECFAFFFF    CALL FTD2XX.FT_IoCtl
00202B74                       |. E8 98FCFFFF    CALL <FTD2XX.FT_ReadEE_ModifChipID>
00202B79                       |. 59             POP ECX
00202B7A                       \. C2 0C00        RETN 0C

FT_ReadEE_ModifChipID:    cmp BYTE PTR DS:[ESP+010h],043h
                          jz FT_ReadEE_ModifChipID_43
                          cmp BYTE PTR DS:[ESP+010h],044h
                          jz FT_ReadEE_ModifChipID_44
                          ret

; cetlo se z adresy 43h, coz je LSB ChipSN
FT_ReadEE_ModifChipID_43: push EAX
            		  mov EAX,DWORD PTR SS:[ESP+8]
            	          mov WORD [EAX],05678h		; modifikace precteneho LSB ChipSN
            		  pop EAX
                          ret

; cetlo se z adresy 44h, coz je MSB ChipSN
FT_ReadEE_ModifChipID_44: push EAX
            	          mov EAX,DWORD PTR SS:[ESP+8]
            	          mov WORD [EAX],01234h		 ; modifikace precteneho MSB ChipSN
           	          pop EAX
                          ret

SafeGuard-IT a USB klíč

Další projekt nabízený společností FTDI je SafeGuard-IT. Jedná se o využití asymetrického šifrování s dvojicí klíčů, veřejným a soukromým. Nejprve se vygeneruje dvojice klíčů – soukromý a veřejný, poté se do uživatelské části interní paměti EEPROM zapíše signatura; zašifrované sériové číslo čipu s využitím soukromého klíče. Uživatel dostane do ruky čip v podobě adaptéru či klíčenky a veřejný klíč, pomocí kterého se v uživatelské aplikaci ověří, zda-li zapsaná signatura v klíči odpovídá veřejnému klíči. Využití této metody má tu výhodu, že stačí jedna verze aplikačního programu pro všechny uživatele a řeší se pouze distribuce veřejných klíčů.

Knihovna SafeGuard-IT.dll je již velmi dobře chráněna proti nežádoucím úpravám, jenže vše opět stojí na čtení sériového čísla čipu funkcí FT_ReadEE. Popsanou úpravou čtení obsahu interní paměti EEPROM tedy dokážeme velmi snadno klonovat obvody FT232R s uloženou signaturou. Abychom klon mohli udělat, musíme mít v ruce klonovaný čip, resp. musíme znát obsah jeho interní paměti EEPROM.

Vzhledem k tomu, že pro práci s USB klíčem se používají stejné ovladače jako pro práci s obvodem FT232R, domnívám se, že se po technické stránce jedná o obvod FT232R zapouzdřený do podoby klíčenky a tudíž platí vše výše uvedené. Porovnávat možnosti USB klíče společnosti FTDI s klíči firem Aladdin (HASP 4), Rainbow (Sentinel SuperPro), Eutronsec (SmartKey) apod., je zbytečná práce. Jedná se o naprosto nesrovnatelné produkty.

FT245R a jeho FTDIChip-ID

S obvodem FT245R (konvertor rozhraní USB a paralelního) jsem neměl možnost pracovat, nicméně z popisu uvedeném v katalogovém listu a z faktu, že používá stejné ovladače jako FT232R soudím, že identifikační číslo a způsob jeho čtení je řešeno shodně s FT232R, a tudíž také platí vše výše uvedené.

Závěr

FT232R je bezpochyby špičkový obvod usnadňující mnohým vývojářům život a umožňující vznik řady zajímavých aplikací. Sériové číslo čipu FT232R uložené v jeho interní paměti EEPROM je bezpochyby výborná vlastnost například pro identifikaci výrobků, ale rozhodně to není prvek, na kterém by bylo vhodné stavět bezpečnostní opatření.

Download & Odkazy

  1. FT232R - USB UART IC http://www.ftdichip.com/Products/FT232R.htm
  2. FTDIChip-ID™ Projects http://www.ftdichip.com/Projects/FTDIChip-ID.htm
  3. SafeGuard-IT Projects http://www.ftdichip.com/Projects/SafeGuard-IT.htm
  4. USB-Key http://www.ftdichip.com/Products/EvaluationKits/USB-Key.htm
Hodnocení článku: 

Komentáře

Dobrá práce. Jak se dalo tušit, takové zabezpečení nemůže nefungovat. Zvláštní, že do takovéhle hračky někdo jde, přestože výrobce neřekne, jak to funguje - takové kupování zajíce v pytli.

Ad SafeGuard-IT: podle popisu výrobce to vypadá, že je to zranitelné stejným způsobem. Ta asymetrická kryptografie v SafeGuard-IT jen zjednodušuje používání, ale stále závisí na nevyčítatelnosti toho tajemství v chipu.

Autor sám přiznává, že se mu nepodařilo přepsat EEPROM, tedy FTDI-Chip v hardware evidentně unikátní je. To, co zde prezentuje, je tuctové hacknutí .DLL tedy autor zaměňuje pojmy, i když výsledek pro nadřazenou aplikaci je identický, prostě FTDI-Chip se dá "měnit".
V praxi, když už útočník umí měnit kód ve spustitelných souborech, nebude se otravovat s funkcí FT_ReadEE, ale rovnou si v .EXE aplikace najde příslušné
FTID_GetChipIDFromHandle (ftHandle, &dwChipID);
if (dwChipID == MojeTajnaHodnota)
a nahradí ho klasickým 5-ti bytovým hackem JMP xxx

A co se týká srovnání s ostatními produkty, tak je to v tomto smyslu všechno stejný šmejd a proti útočníkovi schopného měnit spustitelný kód není šance se ubránit s vyjímkou drahého HW klíče vykonávající části aplikace, respektive zákazníkovi je v HW klíči fakticky vnucen malý počítač.

na firemních stránkách společnosti FTDI stojí tato věta: „The FTDIChip-ID is a permanent, unique number programmed into each IC during production that cannot be changed by the end user“; na unikátnost identifikačního čísla se zde hledí z pohledu konečného uživatele, takže je potřeba brát v úvahu vše, co může konečný uživatel činit. Dokonce byla tato věta psána tak, že konečný uživatel nemůže žádnými prostředky FTDIChip-ID změnit. Nejde ale o věty a slova, ale jde o to, co tyto věty a slovo vyvolají v mysli vývojáře řešícího problematiku ochrany svého nápadu a své práce.

Krom přesného mechanismu tvorby identifikačního čísla FTDIChip-ID, také skutečně prezentuji tuctové cracknutí jedné knihovny .dll, a o to právě jde! Právě jedno tuctové cracknutí obejde případný bezpečnostní prvek důvěřivého vývojáře.

Implementace případného ochranného mechanizmu metodou if (dwChipID == MojeTajnaHodnota) je samozřejmě nejjednodušší a také asi bohužel nejčastější. Pokud např. vývojář použije projekt SafeGuard-IT nebo chráněný program pomocí identifikačního čísla zakryptuje, bude se muset případný útočník s funkcí FT_ReadEE zabývat v té či oné podobě. Buď kvůli tvorbě klonu nebo kvůli dekryptování programu.

Předpokládám, že autor komentáře zná možnosti citovaných HW klíčů, tak může veřejně tvrdit, že se jedná o „všechno stejný šmejd“.

 

Opravdu trefná je ovšem věta „s vyjímkou drahého HW klíče vykonávající části aplikace, respektive zákazníkovi je v HW klíči fakticky vnucen malý počítač“! Jsme na stránkách serveru věnujícímu se z větší části HW problematice a je tedy možné očekávat, že většina vývojářů používá obvod FT232 jako převodník rozhraní mezi PC a vlastní HW aplikací, ve které nezřídka nějaký ten mikropočítač již je. Pokud si tedy hypotetický vývojář na počátku vývoje svého nového projektu přečte, že je možné jedním tuctovým cracknutím .dll obejít FTDIChip-ID, bude přemýšlet o jiném způsobu zabezpečení. A opravdu zákazníkovi nezřídka nutí malý počítač, a zákazník za něj rád zaplatí, protože jinak si např. diagnostiku svého nového vozu se sběrnicemi CAN neudělá. A když už si ten malý počítač koupí a zaplatí, bude ideální tento mikropočítač použít i pro ochranu produktu. Tento příklad s diagnostickým adaptérem jsem uvedl záměrně, Ti co se v této oblasti trochu pohybují, jistě dobře vědí, jaký se kolem klonů diagnostických adaptérů pro automobily točí obchod a kolik to autory stojí marného úsilí, mimo jiné i proto, protože právě zakládají ochranu svého produktu proti klonování na FTDIChip-ID.

Ještě jednou se vrátím k řádce if (dwChipID == MojeTajnaHodnota) – táto řádka je typickým výsledkem návrhu zabezpečení tvořeného v okamžiku, kdy je vlastní produkt již hotov! Pokud vývojář není programátorský laik, pokud si cení své práce a pokud integruje ochranné mechanismy do svého vývoje ve všech jeho etapách, nikdy takovouto řádku nepoužije.

Kde môžem získať tohto súboru?
Nemám žiadny súbor na webe "2006-04-02" dostať.
Rád by som na tomto projekte, ale väčšina.

Vďaka za vašu pomoc.