From: Vincent Le T. <vin...@my...> - 2025-04-26 09:06:46
|
Inside the middleware, there is a minidriver named ciamd.dll What I would suggest is to write a program like the one I wrote here ( https://github.com/vletoux/openpgpmdrv/tree/master/OpenPGPminidriverTest) that connects to the minidriver and realize basic functions (enumerating public keys, certificates, encrypts, change pin, etc). You can add a hook to dump the instructions sent to the card. You can use the following code to hook the SCardTransmit function: void PrintHexToDebug(const BYTE* buffer, DWORD length) { // Allocate memory dynamically TCHAR* hexStr = (TCHAR*)malloc((3 * length + 1) * sizeof(TCHAR)); if (hexStr == NULL) { OutputDebugString(TEXT("Memory allocation failed\n")); return; } for (DWORD i = 0; i < length; i++) { _stprintf_s(&hexStr[i * 3], 4, TEXT("%02X "), buffer[i]); } hexStr[3 * length] = '\0'; OutputDebugString(hexStr); // Free the allocated memory free(hexStr); } LONG WINAPI MySCardTransmit( SCARDHANDLE hCard, LPCSCARD_IO_REQUEST pioSendPci, LPCBYTE pbSendBuffer, DWORD cbSendLength, LPSCARD_IO_REQUEST pioRecvPci, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength ) { // Trace the input buffer OutputDebugString(TEXT("pbSendBuffer: ")); PrintHexToDebug(pbSendBuffer, cbSendLength); OutputDebugString(TEXT("\n")); // Call the original SCardTransmit LONG result = SCardTransmit(hCard, pioSendPci, pbSendBuffer, cbSendLength, pioRecvPci, pbRecvBuffer, pcbRecvLength); // Write the return code as hex TCHAR returnCodeStr[30]; _stprintf_s(returnCodeStr, ARRAYSIZE(returnCodeStr), TEXT("Return code: %08X\n"), result); OutputDebugString(returnCodeStr); // If the return code is successful, dump the output buffer if (result == SCARD_S_SUCCESS && pcbRecvLength && pbRecvBuffer) { (TEXT("pbRecvBuffer: ")); PrintHexToDebug(pbRecvBuffer, *pcbRecvLength); OutputDebugString(TEXT("\n")); } return result; } VOID EnableHook(HMODULE hModule) { HMODULE hScard = LoadLibrary(TEXT("Winscard.dll")); PROC pfnScardTransmit = GetProcAddress(hScard, "SCardTransmit"); PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew); PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)hModule + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while (pImportDesc->Name) { LPCSTR pszModName = (LPCSTR)((BYTE*)hModule + pImportDesc->Name); if (_stricmp(pszModName, "Winscard.dll") == 0) { PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pImportDesc->FirstThunk); while (pThunk->u1.Function) { PROC* ppfn = (PROC*)&pThunk->u1.Function; if (*ppfn == (PROC)pfnScardTransmit) { DWORD oldProtect; VirtualProtect(ppfn, sizeof(PROC), PAGE_EXECUTE_READWRITE, &oldProtect); *ppfn = (PROC)MySCardTransmit; VirtualProtect(ppfn, sizeof(PROC), oldProtect, &oldProtect); } pThunk++; } break; } pImportDesc++; } } And to initialize the minidriver: DWORD Connect(BOOL fSystemDll = TRUE) { DWORD dwReturn = 0; SCARDCONTEXT hSCardContext = NULL; SCARDHANDLE hSCardHandle = NULL; TCHAR szCardModule[256]; TCHAR szReader[256]; DWORD dwCardModuleSize = ARRAYSIZE(szCardModule); DWORD dwReaderSize = ARRAYSIZE(szReader); OPENCARDNAME_EX dlgStruct; PFN_CARD_ACQUIRE_CONTEXT pfnCardAcquireContext; __try { // find a smart card ///////////////////// dwReturn = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hSCardContext); if (SCARD_S_SUCCESS != dwReturn) { __leave; } // Initialize the structure. memset(&dlgStruct, 0, sizeof(dlgStruct)); dlgStruct.dwStructSize = sizeof(dlgStruct); dlgStruct.hSCardContext = hSCardContext; dlgStruct.dwFlags = SC_DLG_MINIMAL_UI; dlgStruct.lpstrRdr = szReader; dlgStruct.nMaxRdr = dwReaderSize; dlgStruct.lpstrCard = szCard; dlgStruct.nMaxCard = ARRAYSIZE(szCard); dlgStruct.lpstrTitle = L"Select Card"; dlgStruct.dwShareMode = 0; // Display the select card dialog box. dwReturn = SCardUIDlgSelectCard(&dlgStruct); if (SCARD_S_SUCCESS != dwReturn) { __leave; } // find the dll path / name //////////////////////////// if (fSystemDll) { dwReturn = SCardGetCardTypeProviderName( hSCardContext, szCard, SCARD_PROVIDER_CARD_MODULE, (PTSTR)&szCardModule, &dwCardModuleSize); if (0 == dwCardModuleSize) { dwReturn = (DWORD)SCARD_E_UNKNOWN_CARD; __leave; } } else { #ifdef _M_X64 _tcscpy_s(szCardModule, dwCardModuleSize, TEXT("Name of the dll.dll")); #else _tcscpy_s(szCardModule, dwCardModuleSize, TEXT("Name of the dll.dll")); #endif } // connect to the smart card //////////////////////////// DWORD dwProtocol, dwState; dwReturn = SCardConnect(hSCardContext, szReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1 | SCARD_PROTOCOL_T0, &hSCardHandle, &dwProtocol); if (SCARD_S_SUCCESS != dwReturn) { __leave; } atr.cbAtr = 32; dwReturn = SCardStatus(hSCardHandle, szReader, &dwReaderSize, &dwState, &dwProtocol, atr.rgbAtr, &atr.cbAtr); if (SCARD_S_SUCCESS != dwReturn) { __leave; } // load //////// if (NULL == (hModule = LoadLibrary(szCardModule))) { dwReturn = GetLastError(); __leave; } if (fSystemDll) { EnableHook(hModule); } if (NULL == (pfnCardAcquireContext = (PFN_CARD_ACQUIRE_CONTEXT)GetProcAddress( hModule, "CardAcquireContext"))) { dwReturn = GetLastError(); __leave; } // initialize context ////////////////////// pCardData = &CardData; pCardData->dwVersion = CARD_DATA_CURRENT_VERSION; pCardData->pfnCspAlloc = _Alloc; pCardData->pfnCspFree = _Free; pCardData->pfnCspReAlloc = _ReAlloc; pCardData->pfnCspCacheAddFile = _CacheAddFileStub; pCardData->pfnCspCacheLookupFile = _CacheLookupFileStub; pCardData->pfnCspCacheDeleteFile = _CacheDeleteFileStub; pCardData->hScard = hSCardHandle; pCardData->hSCardCtx = hSCardContext; pCardData->cbAtr = atr.cbAtr; pCardData->pbAtr = atr.rgbAtr; pCardData->pwszCardName = szCard; //dwReturn = SCardBeginTransaction(hSCardHandle); if (SCARD_S_SUCCESS != dwReturn) { __leave; } dwReturn = pfnCardAcquireContext(pCardData, 0); } __finally { if (dwReturn != 0) { if (hSCardHandle) { SCardEndTransaction(hSCardHandle, SCARD_LEAVE_CARD); SCardDisconnect(hSCardHandle, 0); } if (hSCardContext) SCardReleaseContext(hSCardContext); } } return dwReturn; } DWORD Disconnect() { DWORD dwReturn = 0; if (pCardData) { if (pCardData->hScard) { SCardEndTransaction(pCardData->hScard, SCARD_LEAVE_CARD); SCardDisconnect(pCardData->hScard, 0); } if (pCardData->hSCardCtx) SCardReleaseContext(pCardData->hSCardCtx); pCardData = NULL; } else { dwReturn = SCARD_E_COMM_DATA_LOST; } return dwReturn; } You can then call directly : DWORD GenerateNewKey(DWORD dwIndex) { DWORD dwReturn, dwKeySpec; PIN_ID PinId; __try { if (!pCardData) { dwReturn = SCARD_E_COMM_DATA_LOST; __leave; } switch(dwIndex) { case 0: //Signature, dwKeySpec = AT_SIGNATURE; PinId = ROLE_USER; break; case 2: //Authentication, dwKeySpec = AT_SIGNATURE; PinId = 3; break; case 1: // Confidentiality, dwKeySpec = AT_KEYEXCHANGE; PinId = 4; break; default: dwReturn = SCARD_E_UNEXPECTED; __leave; } dwReturn = pCardData->pfnCardCreateContainerEx(pCardData, (BYTE) dwIndex, CARD_CREATE_CONTAINER_KEY_GEN, dwKeySpec, 1024, NULL, PinId); } __finally { } return dwReturn; } br Vincent Le ven. 25 avr. 2025 à 22:53, Frank Morgner <fra...@gm...> a écrit : > The middleware is available on the bottom of this page > https://www.gov.me/clanak/preuzmite-software-i-uputstva > > But I think you already know that. You analyzed that in 2024 already, > didn't you? > > Regards. > Am 22.04.25 um 14:44 schrieb dzeri96 via Opensc-devel: > > Hello everyone, > > I'm trying to kickstart support for the new Montenegrin eID > <https://www.gov.me/mup/elk>, or at least figure out how it works. I've > sent multiple requests for technical specs to the government, but unless I > take them to court, I doubt I'll get any useful information. Therefore I'll > just write down what I manage to figure out on my own, and hopefully you > can provide further insight. One thing about a country as small as > Montenegro, is that there is a very high probability we didn't implement > anything custom, as it's not financially viable. > > Here's what I have so far: > > - *ATR*: > 3b:dc:96:ff:81:91:fe:1f:c3:80:73:c8:21:13:66:05:03:63:51:00:02:de. It > doesn't seem to comply with the ATR scheme in the IAS ECC specification, > even though the government says the card complies with all EU ID > regulations (unclear which ones). > - *EF.ATR raw data*: 80004301B946040400ECC24703940180 > 4F0BF0496173456363526F6F74E01002 020104020200E6020200E6020200E678 > 0806062B8122F8780282029000 > - *EF.DIR raw data*: 61374F0EE828BD080FD25047656E6572 > 6963500743686970446F63731C300404 025031A004040250324F0EE828BD080F > D2504543432D654944610F4F07A00000 0247100150044943414F61184F0A4D4F > 4E54454E4547524F500A4E6174696F6E 616C4944 > - By deciphering the EF.DIR data, we can discover 4 applications: > - E828BD080FD25047656E65726963 - ECC Generic PKI / ChipDocs Applet > - E828BD080FD2504543432D654944 - ECC eID > - A0000002471001 - ICAO > - 4D4F4E54454E4547524F - Spells out MONTENEGRO in ASCII, label is > "NationalID". No idea what this could be... maybe something related to > healthcare? > - I managed to use npa-tool and read the MRZ stored on the card using > CAN-based PACE, but all other functions of the tool don't work, not even > PIN-based PACE. I'm just using it as an APDU debugger with PACE support. > - The official middleware supplied by the government is Athena > IDProtect. > - The activation software is available here > <https://wapi.gov.me/download/e63b50c5-9ccc-4034-961f-5bb401a9b375?version=1.0>. > It's a java program developed by Mühlbauer <https://www.muehlbauer.de/>. > I decompiled it and saw that it's accessing the ECC eID application. I > managed to extract some APDUs and get the activation status of the card > (PIN change is required on first use). > - iasecc-tool and pkcs15-tool say "Card is invalid or cannot be > handled" regardless of what I try. > > I've skimmed over hundreds of pages of standards, including the ISO-7816 > parts, the NXP ChipDoc v4 spec, the BSI TR-03110, the IAS ECC spec, but I > can barely find any concrete info on these applications. Someone must know > how to access them because there are vendor-provided tools to do so. > > My goals are: > > 1. Get general knowledge about the card and build some PoC APDU chains > to read/set data. > 2. Get the birthdate of the person via PIN-based auth and verify the > authenticity of the data. > 3. Get the openSC suite of tools to work with the card. > 4. Replace the closed-source middleware provided by the government. > > > I would really appreciate any help here. Thanks! > > > _______________________________________________ > Opensc-devel mailing lis...@li...https://lists.sourceforge.net/lists/listinfo/opensc-devel > > _______________________________________________ > Opensc-devel mailing list > Ope...@li... > https://lists.sourceforge.net/lists/listinfo/opensc-devel > |