-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
432 lines (361 loc) · 13.5 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
// thanks @gentilkiwi for reviewing my code and providing the base construct for the improved version!
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winscard.h>
typedef struct _SCARD_DUAL_HANDLE {
SCARDCONTEXT hContext;
SCARDHANDLE hCard;
} SCARD_DUAL_HANDLE, * PSCARD_DUAL_HANDLE;
// allowed block values for safely writing data (assuming non-magic tag)
const BYTE allowedBlocks[] = { 0x01, 0x02, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0A,
0x0C, 0x0D, 0x0E, 0x10, 0x11, 0x12, 0x14, 0x15,
0x16, 0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x1E, 0x20,
0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2A,
0x2C, 0x2D, 0x2E, 0x30, 0x31, 0x32, 0x34, 0x35,
0x36, 0x38, 0x39, 0x3A, 0x3C, 0x3D, 0x3E };
// any block that is not 0x00 or in allBlocks but not in allowedBlocks
const BYTE dangerousSectorBlocks[] = { 0x07, 0x0B, 0x0F, 0x13, 0x17, 0x1B, 0x1F,
0x23, 0x27, 0x2B, 0x2F, 0x33, 0x37, 0x3B, 0x3F };
// for dumping entire card (read only)
const BYTE allBlocks[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F };
void PrintHex(LPCBYTE pbData, DWORD cbData)
{
DWORD i;
for (i = 0; i < cbData; i++)
{
wprintf(L"%02x ", pbData[i]);
}
wprintf(L"\n");
}
BOOL SendRecvReader(PSCARD_DUAL_HANDLE pHandle, const BYTE* pbData, const UINT16 cbData, BYTE* pbResult, UINT16* pcbResult)
{
BOOL status = FALSE;
DWORD cbRecvLenght = *pcbResult;
LONG scStatus;
wprintf(L"> ");
PrintHex(pbData, cbData);
scStatus = SCardTransmit(pHandle->hCard, NULL, pbData, cbData, NULL, pbResult, &cbRecvLenght);
if (scStatus == SCARD_S_SUCCESS)
{
*pcbResult = (UINT16)cbRecvLenght;
wprintf(L"< ");
PrintHex(pbResult, *pcbResult);
status = TRUE;
}
else wprintf(L"%08x\n", scStatus);
return status;
}
BOOL OpenReader(LPCWSTR szReaderName, PSCARD_DUAL_HANDLE pHandle)
{
BOOL status = FALSE;
LONG scStatus;
DWORD dwActiveProtocol;
scStatus = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pHandle->hContext);
if (scStatus == SCARD_S_SUCCESS)
{
scStatus = SCardConnect(pHandle->hContext, szReaderName, SCARD_SHARE_SHARED, SCARD_PROTOCOL_Tx, &pHandle->hCard, &dwActiveProtocol);
if (scStatus == SCARD_S_SUCCESS)
{
status = TRUE;
}
else
{
SCardReleaseContext(pHandle->hContext);
}
}
return status;
}
void CloseReader(PSCARD_DUAL_HANDLE pHandle)
{
SCardDisconnect(pHandle->hCard, SCARD_LEAVE_CARD);
SCardReleaseContext(pHandle->hContext);
}
int WriteToTag(const BYTE* Msg, BYTE block, bool allowUnsafeTargetBlocks) {
bool allowedBlock = false;
if (!allowUnsafeTargetBlocks) {
for (int i = 0; i < sizeof(allowedBlocks); ++i) {
if (allowedBlocks[i] == block) {
allowedBlock = true;
break;
}
}
if (!allowedBlock) {
wprintf(L"Target block is invalid.\n");
return 1;
}
}
// ignore safety and write to given block if true was passed for allowUnsafeTargetBlocks
else {
allowedBlock = true;
}
BYTE APDU_Write[5 + 16] = { 0xff, 0xd6, 0x00, block, 0x10 }; // base command 5 bytes + msg up to 16 bytes
UINT16 apduWriteBaseLength = sizeof(APDU_Write) / sizeof(APDU_Write[0]);
memcpy(APDU_Write + 5, Msg, 16);
// use line below for DEFAULT KEY A
const BYTE APDU_LoadDefaultKey[] = { 0xff, 0x82, 0x00, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
const BYTE APDU_Authenticate_Block[] = { 0xff, 0x86, 0x00, 0x00, 0x05, 0x01, 0x00, block, 0x60, 0x00 };
SCARD_DUAL_HANDLE hDual;
BYTE Buffer[32];
UINT16 cbBuffer; // usually will be 2 (e.g. response 90 00 for success)
// preparations are complete
printf("Writing Hex:\n");
for (UINT16 i = 0; i < 21; ++i) { // first few chars are not part of message that will be written so skip printing them
printf("0x%02X ", APDU_Write[i]);
}
printf("\n");
// my Laptop: "ACS ACR122U PICC Interface 0"
// my PC: "ACS ACR122 0"
if (OpenReader(L"ACS ACR122 0", &hDual))
{
cbBuffer = 2;
if (SendRecvReader(&hDual, APDU_LoadDefaultKey, sizeof(APDU_LoadDefaultKey), Buffer, &cbBuffer))
{
wprintf(L"Default Key A has been loaded.\n");
}
// make sure this operation was successful, terminate if not
if (!(Buffer[0] == 0x90 && Buffer[1] == 0x00)) {
CloseReader(&hDual);
wprintf(L"Error code received. Aborting..\n");
free(APDU_Write);
return 1;
}
cbBuffer = 2;
if (SendRecvReader(&hDual, APDU_Authenticate_Block, sizeof(APDU_Authenticate_Block), Buffer, &cbBuffer))
{
wprintf(L"Block has been authenticated.\n");
}
// make sure this operation was successful, terminate if not
if (!(Buffer[0] == 0x90 && Buffer[1] == 0x00)) {
CloseReader(&hDual);
wprintf(L"Error code received. Aborting..\n");
free(APDU_Write);
return 1;
}
cbBuffer = 2;
if (SendRecvReader(&hDual, APDU_Write, 23, Buffer, &cbBuffer))
{
wprintf(L"Data has successfully been written to the block!\n");
}
// make sure this operation was successful, terminate if not
if (!(Buffer[0] == 0x90 && Buffer[1] == 0x00)) {
CloseReader(&hDual);
wprintf(L"Error code received. Aborting..\n");
free(APDU_Write);
return 1;
}
CloseReader(&hDual);
}
else {
wprintf(L"Failed to find NFC reader.\n");
}
//free(APDU_Write);
return 0;
}
int ReadFromTag(BYTE block, bool dump) {
const BYTE APDU_Read[] = { 0xff, 0xb0, 0x00, block, 0x10 }; // page 16 of ACR122U_APIDriverManual.pdf (reads 16 bytes, and the page number is a coincidence)
UINT16 apduReadLength = sizeof(APDU_Read) / sizeof(APDU_Read[0]);
const BYTE APDU_LoadDefaultKey[] = { 0xff, 0x82, 0x00, 0x00, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
const BYTE APDU_Authenticate_Block[] = { 0xff, 0x86, 0x00, 0x00, 0x05, 0x01, 0x00, block, 0x60, 0x00 };
SCARD_DUAL_HANDLE hDual;
BYTE Buffer[18]; // return status (success 90 00 or failure 63 00) takes 2 bytes, received data takes 16 bytes
UINT16 cbBuffer; // usually will be 2 (e.g. response 90 00 for success)
// my Laptop: "ACS ACR122U PICC Interface 0"
// my PC: "ACS ACR122 0"
if (OpenReader(L"ACS ACR122 0", &hDual))
{
cbBuffer = 2;
if (SendRecvReader(&hDual, APDU_LoadDefaultKey, sizeof(APDU_LoadDefaultKey), Buffer, &cbBuffer))
{
wprintf(L"Default Key A has been loaded.\n");
}
// make sure this operation was successful, terminate if not
if (!(Buffer[0] == 0x90 && Buffer[1] == 0x00)) {
CloseReader(&hDual);
wprintf(L"Error code received. Aborting..\n");
return 1;
}
cbBuffer = 2;
if (SendRecvReader(&hDual, APDU_Authenticate_Block, sizeof(APDU_Authenticate_Block), Buffer, &cbBuffer))
{
wprintf(L"Block has been authenticated.\n");
}
// make sure this operation was successful, terminate if not
if (!(Buffer[0] == 0x90 && Buffer[1] == 0x00)) {
CloseReader(&hDual);
wprintf(L"Error code received. Aborting..\n");
return 1;
}
cbBuffer = 18;
if (SendRecvReader(&hDual, APDU_Read, apduReadLength, Buffer, &cbBuffer))
{
// PrintHex(Buffer, sizeof(Buffer));
// check for success (here the success code is stored after the data read, so read the last two bytes of the buffer)
if (!(Buffer[16] == 0x90 && Buffer[17] == 0x00)) {
CloseReader(&hDual);
wprintf(L"Error code received. Aborting..\n");
return 1;
}
}
else {
wprintf(L"Failed to read block.");
CloseReader(&hDual);
return 1;
}
// print actual characters in cmd if we are not dumping the full card
if (!dump) {
wprintf(L"Successfully read block.");
wprintf(L"Trying to print read data in human-readable form:\n");
for (int i = 0; i < 16; i++) { // any text would have ended with \n so dont read last Byte
printf("%c ", Buffer[i]);
}
printf("\n");
}
// if dump then write read data to fille
if (dump) {
// open file
FILE* fp;
int err = fopen_s(&fp, "dump.txt", "a");
if (err != 0) {
printf("Error opening the file.\n");
CloseReader(&hDual);
return 1;
}
if (fp != NULL) {
// each line of the file should start with block #
fprintf(fp, "[%02X] ", block);
// write from BUFFER (but not last two status bytes)
for (int i = 0; i < 16; i++) {
fprintf(fp, "%02X ", Buffer[i]);
}
fprintf(fp, "\n"); // newline to prepare for next iteration
// close file
fclose(fp);
}
else {
printf("ERROR: FD is zero. Stopping execution..\n");
CloseReader(&hDual);
return 1;
}
}
CloseReader(&hDual);
}
else {
wprintf(L"Failed to find NFC reader.\n");
}
return 0;
}
int ResetCardContents() {
// empty every writable block
const BYTE Msg[16] = { 0x00 };
// get size of global allowedBlocks array (amount of elements)
UINT16 arraySize = sizeof(allowedBlocks) / sizeof(allowedBlocks[0]);
for (UINT16 i = 0; i < arraySize; ++i) {
UINT16 status_code = WriteToTag(Msg, allowedBlocks[i], false);
if (status_code != 0) {
printf("Error occured while writing! Terminating.");
return 1;
}
}
printf("\nSuccessfully reset card contents.\n");
return 0;
}
int DumpCard() {
// get size of global allowedBlocks array (amount of elements)
UINT16 arraySize = sizeof(allBlocks) / sizeof(allBlocks[0]);
for (UINT16 i = 0; i < arraySize; ++i) {
UINT16 status_code = ReadFromTag(allBlocks[i], true);
if (status_code != 0) {
printf("Error occured while trying to dump card! Terminating.");
return 1;
}
// wait between iterations to avoid errors with opening/closing file
Sleep(100); // 100 ms usually works, shorter times can lead to a failed dump
}
printf("\nSuccessfully dumped card content to dump.txt.\n");
return 0;
}
// formats the card for NDEF (only works once because it assumes key A of every sector to be the default one
// after running this code the mifare classic 1k becomes compatible with almost every modern smartphone!! :)
int FormatNDEF() {
// STEP 1: Adjust block 1
const BYTE block1msg[16] = { 0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 };
BYTE block1Block = 0x01;
int step1Success = WriteToTag(block1msg, block1Block, false);
if (step1Success != 0) {
printf("Error occurred while writing to block 1 of sector 0! Terminating.");
return 1;
}
Sleep(100);
// STEP 2: Adjust block 2
const BYTE block2msg[16] = { 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 };
BYTE block2Block = 0x02;
int step2Success = WriteToTag(block2msg, block2Block, false);
if (step2Success != 0) {
printf("Error occurred while writing to block 2 of sector 0! Terminating.");
return 1;
}
Sleep(100);
// STEP 3: Adjust trailer sector 0
const BYTE SectorZeroMsg[16] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// const BYTE SectorZeroMsg[16] = { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A };
BYTE SectorZeroBlock = 0x03;
int step3Success = WriteToTag(SectorZeroMsg, SectorZeroBlock, true);
if (step3Success != 0) {
printf("Error occurred while writing trailer sector 0! Terminating.");
return 1;
}
Sleep(100);
// STEP 4: Write empty NDEF message to block 0 of sector 1
const BYTE NDEFmsg[16] = { 0x03, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE NDEFmsgBlock = 0x04;
int step4Success = WriteToTag(NDEFmsg, NDEFmsgBlock, false);
if (step4Success != 0) {
printf("Error occurred while writing to block 0 of sector 1! Terminating.");
return 1;
}
Sleep(100);
// STEP 5: Adjust trailer sectors of sectors 1-15
const BYTE Msg[16] = { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
// get size of global dangerousSectorBlocks array (amount of elements)
UINT16 arraySize = sizeof(dangerousSectorBlocks) / sizeof(dangerousSectorBlocks[0]);
for (UINT16 i = 0; i < arraySize; ++i) {
UINT16 status_code = WriteToTag(Msg, dangerousSectorBlocks[i], true);
if (status_code != 0) {
printf("Error occurred while writing! Terminating.");
return 1;
}
// wait between iterations to be safe (might not be required tho)
Sleep(100);
}
printf("\nSuccessfully NDEF formatted the card.\n");
return 0;
}
int main() {
//const BYTE Msg[16] = { 0x00 }; // empty the given block
// block 01
//const BYTE Msg[16] = { 0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 };
//BYTE block = 0x01;
// block 02
//const BYTE Msg[16] = { 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 };
//BYTE block = 0x02;
// ----------------------
// determine size of array now (because you cant do that later on from a pointer)
//UINT16 msgSize = sizeof(Msg) / sizeof(Msg[0]);
// ----------------------
// choose function to run (uncomment):
// WriteToTag(Msg, block, false);
// ReadFromTag(block, false); // true / false (store block content in file dump.txt / dont)
// ResetCardContents(); // only works with default keys / sector trailers
// DumpCard();
FormatNDEF();
return 0;
}