├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── INSTALL ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── copyright └── rules ├── dictionary.c ├── dictionary.h ├── dictionary.txt ├── dictionary_parser.l ├── mac.c ├── mac.h ├── mfc-spec.txt ├── mfterm.c ├── mfterm.h ├── mfterm.man ├── mifare.c ├── mifare.h ├── mifare_ctrl.c ├── mifare_ctrl.h ├── spec_parser.y ├── spec_syntax.c ├── spec_syntax.h ├── spec_tokenizer.l ├── tag.c ├── tag.h ├── term_cmd.c ├── term_cmd.h ├── util.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | mfterm 2 | .depends 3 | .deps 4 | *.o 5 | *.a 6 | *~ 7 | *.bak 8 | Makefile 9 | Makefile.in 10 | aclocal.m4 11 | autom4te.cache 12 | config.h 13 | config.h.in 14 | config.log 15 | config.status 16 | configure 17 | compile 18 | ylwrap 19 | depcomp 20 | install-sh 21 | stamp-h1 22 | missing 23 | libdp_a-dictionary_parser.c 24 | libsp_a-spec_tokenizer.c 25 | libsp_a-spec_parser.c 26 | libsp_a-spec_parser.h 27 | debian/files 28 | debian/mfterm.debhelper.log 29 | debian/mfterm.substvars 30 | 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | script: ./autogen.sh && ./configure && make 3 | before_install: 4 | - sudo apt-get -qq update 5 | - sudo apt-get install -y libusb-dev 6 | install: 7 | - wget https://github.com/nfc-tools/libnfc/releases/download/libnfc-1.7.1/libnfc-1.7.1.tar.bz2 8 | - tar jxf libnfc-1.7.1.tar.bz2 9 | - pushd libnfc-1.7.1 && ./configure && make && sudo make install && popd 10 | 11 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Anders Sundman 2 | Markus Näsman 3 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation of mfterm 2 | 3 | If you got the sources from git, run "./autogen.sh" to have the auto 4 | tools generate some files requried for the build. If you got the 5 | source from a source code distribution, then you do not have to 6 | perform this step. 7 | 8 | To build and install mfterm issue the commands: 9 | 10 | $ ./autogen.sh # If using the sources from the git repo 11 | $ ./configure # Possibly with some options, use --help 12 | $ make 13 | $ make install # Possibly using sudo 14 | 15 | Dependencies for building mfterm are: libnfc (1.7.0), libreadline, 16 | libcrypto, OpenSSL, bison, flex. 17 | 18 | Trouble shooting: 19 | 20 | If you get error messages when trying to read a tag. Verify that 21 | libnfc is installed and configured correctly with the 'nfc-list' 22 | utility program (supplied with libnfc). It should not give you any 23 | error messages and if you place a tag on your reader, it should be 24 | detected. 25 | 26 | When building, if you get the error message: 27 | "error: implicit declaration of function ‘rpl_malloc’" 28 | 29 | Try this: after running the autogen, but before running the configure, 30 | set the shell variable: 31 | 32 | $ export ac_cv_func_malloc_0_nonnull=yes 33 | 34 | then run configure, make, make install again. 35 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012 Anders Sundman 2 | # 3 | # This file is part of mfterm. 4 | # 5 | # mfterm is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # mfterm is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with mfterm. If not, see . 17 | 18 | AM_CFLAGS = -g -Wall -Wconversion -std=c99 -Werror 19 | 20 | SUBDIRS = . 21 | 22 | bin_PROGRAMS = mfterm 23 | 24 | mfterm_SOURCES = \ 25 | mfterm.h mfterm.c \ 26 | term_cmd.h term_cmd.c \ 27 | util.h util.c \ 28 | tag.h tag.c \ 29 | mifare.h mifare.c \ 30 | mifare_ctrl.h mifare_ctrl.c \ 31 | dictionary.h dictionary.c \ 32 | spec_syntax.h spec_syntax.c \ 33 | mac.h mac.c 34 | 35 | mfterm_LDADD = libdp.a libsp.a -lreadline -lnfc -lcrypto 36 | 37 | man1_MANS = mfterm.man 38 | dist_man1_MANS = mfterm.man 39 | 40 | # We create two convenience libraries fot the parser targets. This is 41 | # to allow us to use different flags (since automake doesn't support 42 | # per file, but only per target flags). 43 | noinst_LIBRARIES = libdp.a libsp.a 44 | 45 | libdp_a_SOURCES = dictionary_parser.l 46 | libdp_a_LFLAGS = --prefix=dp_ -o$(LEX_OUTPUT_ROOT).c 47 | libdp_a_CFLAGS = -g -std=c99 -Wall -Wno-unused-function -Wno-unused-but-set-variable -Wno-implicit-function-declaration 48 | 49 | libsp_a_SOURCES = spec_tokenizer.l spec_parser.y 50 | libsp_a_LFLAGS = --prefix=sp_ -o$(LEX_OUTPUT_ROOT).c 51 | libsp_a_YFLAGS = --name-prefix=sp_ -d 52 | libsp_a_CFLAGS = -g -std=c99 -Wall -Wno-unused-function -Wno-unused-but-set-variable -Wno-implicit-function-declaration 53 | 54 | BUILT_SOURCES = libsp_a-spec_parser.h 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mfterm [![Build Status](https://travis-ci.org/4ZM/mfterm.svg?branch=master)](https://travis-ci.org/4ZM/mfterm) 2 | 3 | mfterm is a terminal interface for working with Mifare Classic tags. 4 | 5 | Installation 6 | ----------- 7 | ### Mac OS 8 | 9 | `brew install mfterm` 10 | 11 | ### Linux 12 | 13 | Precompiled package in [Kali Linux](https://www.kali.org/) 14 | 15 | Usage 16 | ----------- 17 | Tab completion on commands is available. Also, commands that have file 18 | name arguments provide tab completion on files. There is also a 19 | command history, like in most normal shells. 20 | 21 | Working with the mfterm program there are a few state variables that 22 | are used. 23 | 24 | Current Tag 25 | ----------- 26 | The "current tag" is populated with the 'load' or 'read' commands. The 27 | 'read' command will read data from a physical tag and requires the 28 | "current keys" to be set to the keys of the tag. Clear the "current 29 | tag" by using the 'clear' command. 30 | 31 | Display the "current tag" by using the 'print' command. The keys of 32 | the "current tag" are displayed with the 'print keys' command. Note: 33 | the tag keys could be different from the "current keys" displayed by 34 | the 'keys print' command. 35 | 36 | The data of the "current tag" can be manipulated with the 'set' 37 | command. 38 | 39 | The "current tag" can be persisted by writing it to a file with the 40 | 'save' command. It can also be written to a physical tag with the 41 | 'write' command. For the 'write' command to succeed, the "current 42 | keys" have to be set to appropriate values. The 'write unlocked' 43 | command can be used to write to block 0 on some 1k pirate cards. 44 | 45 | If you are reading or loading a 1k tag, the mfterm program will still 46 | use a full 4k tag to represent it. The last 3k will be all 47 | zeroes. This is in analogy with the other libnfc tools. 48 | 49 | Current Keys 50 | ------------ 51 | The "current keys" are used to authenticate when performing operations 52 | on a physical tag. They can be displayed using the 'keys' 53 | command. Clear the "current keys" by using the 'keys clear' command. 54 | 55 | The keys are stored just like a tag in a file using the 'keys save', 56 | but with all the data fields except the sector trailers cleared. The 57 | keys can be loaded from a file, either a real tag dump or a key tag 58 | dump, with the 'keys load' command. 59 | 60 | The "current keys" can be set to match the "current tag" by using the 61 | 'keys import' command. It is also possible to manually set a key using 62 | the 'keys set' command. 63 | 64 | Use the 'keys test' command to test if the "current keys" can be used 65 | to authenticate with a physical tag. 66 | 67 | Dictionary 68 | ---------- 69 | A key dictionary can be imported from a file using the 'dict load' 70 | command. This dictionary can then be used to perform a dictionary 71 | attack on the sectors of a tag by using the 'dict attack' command. 72 | 73 | The format of the dictionary file is simple. One key (6 bytes, 12 hex 74 | characters) per line and # is a comment. 75 | 76 | Performing 'dict load' on several files will produce a dictionary that 77 | is the union of those files. Duplicates will be removed. 78 | 79 | To list all the keys in the dictionary, use the command 'dict'. To 80 | clear the dictionary use 'dict clear'. 81 | 82 | Other commands 83 | -------------- 84 | Quit the mfterm program by issuing the 'quit' command. 85 | 86 | Help is available by writing 'help' 87 | 88 | 89 | MAC Computation 90 | --------------- 91 | 92 | The function 'mac compute' is used for computing DES MACs (message 93 | authentication codes). They require a 64 bit key that can be set using 94 | the command 'mac key'. The same command, without arguments, is used to 95 | display the current key. 96 | 97 | The input to the DES MAC is UID + 14 left most bytes of the specified 98 | block. 99 | 100 | Using the command 'mac update' is shorthand for a MAC computation and 101 | then setting the MAC of the same block. 102 | 103 | Specification Files 104 | ------------------- 105 | A specification file defines names for parts of the tag data. See the 106 | file mfc-spec.txt for a sample specification. 107 | 108 | Specification files are loaded with the command 'spec load'. They can 109 | be cleared with 'spec clear'. To display the data structure loaded use 110 | the command 'spec'. 111 | 112 | Once a specification has been loaded, it can be used to access the 113 | data in the tag by using a specification path. In the sample 114 | specification, the path: '.sector_0.block_0.atqa', when entered in the 115 | terminal, will display the two bytes of data starting with byte 6. 116 | 117 | 118 | Building mfterm 119 | --------------- 120 | 121 | Standard: ./configure; make; make install 122 | 123 | See INSTALL file for details. 124 | 125 | 126 | WARNING: 127 | -------- 128 | The mfterm software is neither thoroughly tested nor widely used. It 129 | likely contains a number of serious bugs that can be exploited to 130 | compromise your computer. Do NOT run the mfterm software as a 131 | privileged user (e.g. root), and ONLY load tag, dictionary and 132 | specification files that you get from people you trust. 133 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | aclocal 4 | autoheader 5 | automake --add-missing --copy --foreign 6 | autoconf 7 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012 Anders Sundman 2 | # 3 | # This file is part of mfterm. 4 | # 5 | # mfterm is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # mfterm is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with mfterm. If not, see . 17 | 18 | AC_PREREQ([2.67]) 19 | AC_INIT([mfterm], [1.0.7], [anders@4zm.org]) 20 | AC_CONFIG_SRCDIR([mfterm.c]) 21 | AC_CONFIG_HEADERS([config.h]) 22 | 23 | AM_INIT_AUTOMAKE 24 | 25 | # Checks for programs. 26 | AC_CHECK_PROGS(YACC, 'bison -y' byacc yacc, :) 27 | if test "x$YACC" == "x:"; then 28 | AC_MSG_ERROR([Bison or some other YACC compatible parser generator is required.]) 29 | fi 30 | AC_PROG_YACC 31 | 32 | AC_PROG_CC 33 | 34 | AC_CHECK_PROGS(LEX, flex lex, :) 35 | if test "x$LEX" == "x:"; then 36 | AC_MSG_ERROR([Lex or Flex or is required.]) 37 | fi 38 | AM_PROG_LEX 39 | 40 | AC_PROG_MAKE_SET 41 | AC_PROG_RANLIB 42 | AM_PROG_CC_C_O 43 | 44 | # Checks for libraries. 45 | AC_CHECK_LIB([nfc], [nfc_open], [], 46 | [AC_MSG_ERROR([libnfc >= 1.7 is required (http://www.libnfc.org/download)])]) 47 | 48 | AC_CHECK_LIB([readline], [rl_completion_matches], [have_readline=yes], 49 | [AC_MSG_ERROR([libreadline is required])]) 50 | 51 | if test $have_readline = yes 52 | then 53 | AC_EGREP_HEADER([int rl_completion_suppress_append], 54 | [readline/readline.h], 55 | AC_DEFINE(HAVE_RL_COMPLETION_SUPPRESS_APPEND, 1, [Define if you have rl_completion_suppress_append]), 56 | AC_MSG_WARN([Command completion support limited by installed readline library])) 57 | fi 58 | 59 | AC_CHECK_LIB([crypto], [DES_set_key_unchecked], [HAVE_LIBCRYPTO=yes], 60 | [AC_MSG_ERROR([libcrypto is required])]) 61 | 62 | # Checks for header files. 63 | AC_CHECK_HEADERS([stddef.h stdint.h stdlib.h string.h strings.h], [], 64 | [AC_MSG_ERROR([A required header file was not found.])]) 65 | 66 | # Checks for typedefs, structures, and compiler characteristics. 67 | AC_HEADER_STDBOOL 68 | AC_TYPE_SIZE_T 69 | AC_TYPE_UINT8_T 70 | AC_CHECK_TYPES([ptrdiff_t]) 71 | 72 | # Checks for library functions. 73 | AC_FUNC_MALLOC 74 | AC_CHECK_FUNCS([memset strcasecmp strchr strdup strtol strtoul], [], 75 | [AC_MSG_ERROR([A required function was not found.])]) 76 | 77 | 78 | AC_CONFIG_FILES([Makefile]) 79 | 80 | AC_OUTPUT 81 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | mfterm (1.0.2) unstable; urgency=low 2 | 3 | * Initial release for Debian. 4 | 5 | -- Markus Näsman Wed, 02 Jan 2013 21:08:35 +0100 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: mfterm 2 | Section: utils 3 | Priority: extra 4 | Maintainer: Markus Näsman 5 | Build-Depends: debhelper (>= 8.0.0), automake, libssl-dev, libnfc-dev, libnfc4, hardening-wrapper, libusb-0.1-4, libpcsclite1, mime-support, m4, libsigsegv2, perl-modules, flex, gawk, libreadline-dev, bison 6 | Standards-Version: 3.9.3 7 | Homepage: https://github.com/4ZM/mfterm 8 | Vcs-Git: git://github.com/4ZM/mfterm.git 9 | 10 | Package: mfterm 11 | Architecture: any 12 | Depends: ${misc:Depends}, ${shlibs:Depends} 13 | Description: Terminal for working with Mifare Classic 1-4k Tags 14 | mfterm is a terminal interface for working with Mifare Classic tags. 15 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: mfterm 3 | 4 | Files: * 5 | Copyright: 2011 Anders Sundman , 2013 Markus Näsman 6 | License: GPL-3.0+ 7 | 8 | Files: debian/* 9 | Copyright: 2013 Markus Näsman 10 | License: GPL-3.0+ 11 | 12 | License: GPL-3.0+ 13 | This program is free software: you can redistribute it and/or modify 14 | it under the terms of the GNU General Public License as published by 15 | the Free Software Foundation, either version 3 of the License, or 16 | (at your option) any later version. 17 | . 18 | This package is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | GNU General Public License for more details. 22 | . 23 | You should have received a copy of the GNU General Public License 24 | along with this program. If not, see . 25 | . 26 | On Debian systems, the complete text of the GNU General 27 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 28 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | override_dh_auto_configure: 13 | ./autogen.sh 14 | dh_auto_configure 15 | 16 | %: 17 | dh $@ 18 | -------------------------------------------------------------------------------- /dictionary.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "dictionary.h" 24 | 25 | static key_list_t* key_list = NULL; 26 | 27 | key_list_t* kl_add(key_list_t** list, const uint8_t* key); 28 | void kl_clear(key_list_t** list); 29 | 30 | key_list_t* kl_make_node(const uint8_t* key); 31 | int key_cmp(const uint8_t* k1, const uint8_t* k2); 32 | 33 | void dictionary_clear() { 34 | kl_clear(&key_list); 35 | } 36 | 37 | int dictionary_add(const uint8_t* key) { 38 | return kl_add(&key_list, key) != NULL; 39 | } 40 | 41 | key_list_t* dictionary_get() { 42 | return key_list; 43 | } 44 | 45 | // Append a node to the list. Don't append duplicates, O(n) operation. 46 | key_list_t* kl_add(key_list_t** list, const uint8_t* key) { 47 | if (list == NULL) 48 | return NULL; 49 | 50 | // A new list 51 | if (*list == NULL) 52 | return *list = kl_make_node(key); 53 | 54 | // Append (but no duplicates) 55 | key_list_t* it = *list; 56 | key_list_t* last = NULL; 57 | while(it) { 58 | 59 | // Don't add duplicates, but move the key first in the list 60 | if (key_cmp(it->key, key) == 0) { 61 | if (last) { 62 | last->next = it->next; // disconnect it 63 | it->next = *list; // move it fisrt 64 | *list = it; // re-point the head 65 | } 66 | 67 | return NULL; // Return null on duplicates 68 | } 69 | last = it; 70 | it = it->next; 71 | } 72 | 73 | return last->next = kl_make_node(key); 74 | } 75 | 76 | void kl_clear(key_list_t** list) { 77 | if (list == NULL || *list == NULL) 78 | return; 79 | 80 | // Free the list nodes 81 | key_list_t* it = *list; 82 | do { 83 | key_list_t* next = it->next; 84 | free(it); 85 | it = next; 86 | } while(it); 87 | 88 | *list = 0; 89 | } 90 | 91 | key_list_t* kl_make_node(const uint8_t* key) { 92 | // Create the list node 93 | key_list_t* new_node = (key_list_t*) malloc(sizeof(key_list_t)); 94 | memcpy((void*)new_node, key, 6); 95 | new_node->next = NULL; 96 | return new_node; 97 | } 98 | 99 | int key_cmp(const uint8_t* k1, const uint8_t* k2) { 100 | for (int i = 0; i < 6; ++i) { 101 | if (k1[i] != k2[i]) 102 | return -1; 103 | } 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /dictionary.h: -------------------------------------------------------------------------------- 1 | #ifndef DICTIONARY__H 2 | #define DICTIONARY__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | typedef struct key_list_t_ { 27 | uint8_t key[6]; 28 | struct key_list_t_* next; 29 | } key_list_t; 30 | 31 | /** 32 | * Parse the input file and import all keys found in the dictionary. 33 | */ 34 | int dictionary_import(FILE* input); 35 | 36 | /** 37 | * Clear the dictionary and free all allocated memory. 38 | */ 39 | void dictionary_clear(); 40 | 41 | /** 42 | * Add a new key to the dictionary. If the dictionary does not exist 43 | * (is empty), it will be created and the key inserted. If the key 44 | * already exists in the list, it will be moved to the head of the 45 | * list and 0 will be returned; else != 0 is returned. 46 | * Note: this operation is O(n) 47 | */ 48 | int dictionary_add(const uint8_t* key); 49 | 50 | /** 51 | * Return a head pointer to the current dictionary (or NULL if it is 52 | * empty). Don't hang on to this pointer after an add operation, since 53 | * the list head migt change; rather, use this function again 54 | * everywhere a ref. to the list is required. 55 | */ 56 | key_list_t* dictionary_get(); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /dictionary.txt: -------------------------------------------------------------------------------- 1 | # More well known keys! 2 | # Standard keys 3 | FFFFFFFFFFFF 4 | A0A1A2A3A4A5 5 | D3F7D3F7D3F7 6 | 000000000000 7 | 8 | # Keys from mfoc 9 | B0B1B2B3B4B5 10 | 4D3A99C351DD 11 | 1A982C7E459A 12 | AABBCCDDEEFF 13 | 714C5C886E97 14 | 587EE5F9350F 15 | A0478CC39091 16 | 533CB6C723F6 17 | 8FD0A4F256E9 18 | 19 | # Keys from: 20 | # http://pastebin.com/wcTHXLZZ 21 | A64598A77478 22 | 26940B21FF5D 23 | FC00018778F7 24 | 00000FFE2488 25 | 5C598C9C58B5 26 | E4D2770A89BE 27 | 28 | # Keys from: 29 | # http://pastebin.com/svGjN30Q 30 | 434F4D4D4F41 31 | 434F4D4D4F42 32 | 47524F555041 33 | 47524F555042 34 | 505249564141 35 | 505249564142 36 | 37 | # Keys from: 38 | # http://pastebin.com/d7sSetef 39 | 0297927C0F77 40 | EE0042F88840 41 | 722BFCC5375F 42 | F1D83F964314 43 | 44 | # Keys from: 45 | # http://pastebin.com/pvJX0xVS 46 | 54726176656C 47 | 776974687573 48 | 4AF9D7ADEBE4 49 | 2BA9621E0A36 50 | 51 | # Keys from: 52 | # http://pastebin.com/y3PDBWR1 53 | 000000000001 54 | 123456789ABC 55 | B127C6F41436 56 | 12F2EE3478C1 57 | 34D1DF9934C5 58 | 55F5A5DD38C9 59 | F1A97341A9FC 60 | 33F974B42769 61 | 14D446E33363 62 | C934FE34D934 63 | 1999A3554A55 64 | 27DD91F1FCF1 65 | A94133013401 66 | 99C636334433 67 | 43AB19EF5C31 68 | A053A292A4AF 69 | 505249565441 70 | 505249565442 71 | 72 | # Keys from: 73 | # http://pastebin.com/TUXj17K3 74 | FC0001877BF7 75 | 76 | # Keys from: 77 | # http://0x9000.blogspot.com/2010/12/mifare-classic-default-keys.html 78 | A0B0C0D0E0F0 79 | A1B1C1D1E1F1 80 | 81 | # Keys from: 82 | # https://code.google.com/p/mifare-key-cracker/downloads/list 83 | BD493A3962B6 84 | 010203040506 85 | 111111111111 86 | 222222222222 87 | 333333333333 88 | 444444444444 89 | 555555555555 90 | 666666666666 91 | 777777777777 92 | 888888888888 93 | 999999999999 94 | AAAAAAAAAAAA 95 | BBBBBBBBBBBB 96 | CCCCCCCCCCCC 97 | DDDDDDDDDDDD 98 | EEEEEEEEEEEE 99 | 0123456789AB 100 | 101 | # Keys from: 102 | # https://github.com/4ZM/mfterm/blob/master/dictionary.txt 103 | 000000000002 104 | 00000000000A 105 | 00000000000B 106 | 100000000000 107 | 200000000000 108 | A00000000000 109 | B00000000000 110 | 111 | # Key from: 112 | # ladyada.net 113 | ABCDEF123456 114 | 115 | # Key from: 116 | # http://irq5.io/2013/04/13/decoding-bcard-conference-badges/ 117 | F4A9EF2AFC6D 118 | 119 | # Keys from: 120 | # https://github.com/iceman1001/proxmark 121 | 4B0B20107CCB 122 | 569369C5A0E5 123 | 632193BE1C3C 124 | 644672BD4AFE 125 | 8FE644038790 126 | 9DE89E070277 127 | B5FF67CBA951 128 | EFF603E1EFE9 129 | F14EE7CAE863 130 | 44AB09010845 131 | 85FED980EA5A 132 | 314B49474956 133 | 564C505F4D41 134 | 0263DE1278F3 135 | 067DB45454A9 136 | 0DB5E6523F7C 137 | 100533B89331 138 | 136BDB246CAC 139 | 15FC4C7613FE 140 | 16F21A82EC84 141 | 16F3D5AB1139 142 | 17758856B182 143 | 186D8C4B93F9 144 | 1FC235AC1309 145 | 22C1BAE1AACD 146 | 243F160918D1 147 | 25094DF6F148 148 | 2735FC181807 149 | 2A3C347A1200 150 | 2ABA9519F574 151 | 324F5DF65310 152 | 32AC3B90AC13 153 | 35C3D2CAEE88 154 | 3A42F33AF429 155 | 3A4BBA8ADAF0 156 | 3DF14C8000A1 157 | 3E3554AF0E12 158 | 3E65E4FB65B3 159 | 40EAD80721CE 160 | 454841585443 161 | 460722122510 162 | 48FFE71294A0 163 | 491CDCFB7752 164 | 4AD1E273EAF1 165 | 4B791BEA7BCC 166 | 51119DAE5216 167 | 51284C3686A6 168 | 528C9DFFE28C 169 | 5EB8F884C8D1 170 | 5F146716E373 171 | 6202A38F69E2 172 | 6338A371C0ED 173 | 63F17A449AF0 174 | 643FB6DE2217 175 | 64E3C10394C2 176 | 653A87594079 177 | 67362D90F973 178 | 682D401ABB09 179 | 68D30288910A 180 | 693143F10368 181 | 6A470D54127C 182 | 740E9A4F9AAF 183 | 75CCB59C9BED 184 | 75D8690F21B6 185 | 75EDE6A84460 186 | 7DE02A7F6025 187 | 82F435DEDF01 188 | 83E3549CE42D 189 | 84FD7F7A12B6 190 | 85675B200017 191 | 871B8C085997 192 | 8765B17968A2 193 | 937A4FFF3011 194 | 97184D136233 195 | 97D1101F18B0 196 | 9AFA6CB4FC3D 197 | 9AFC42372AF1 198 | 9F131D8C2057 199 | A27D3804C259 200 | A3F97428DD01 201 | A8966C7CC54B 202 | A9F953DEF0A3 203 | AAFB06045877 204 | AC0E24C75527 205 | AE3FF4EEA0DB 206 | B0C9DD55DD4D 207 | B20B83CB145C 208 | B736412614AF 209 | BF23A53C1F63 210 | C4652C54261C 211 | C6AD00254562 212 | C7C0ADB3284F 213 | C82EC29E3235 214 | CB9A1F2D7368 215 | D39BB83F5297 216 | D49E2826664F 217 | D8A274B2E026 218 | DF27A8F1CB8E 219 | E2C42591368A 220 | E3429281EFC1 221 | E444D53D359F 222 | F124C2578AD0 223 | F59A36A2546D 224 | FEE470A4CB58 225 | 0000000018DE 226 | 0000014B5C31 227 | 003003003003 228 | 003CC420001A 229 | 013889343891 230 | 01FA3FC68349 231 | 021209197591 232 | 050908080008 233 | 0A7932DC7E65 234 | 0C669993C776 235 | 0C71BCFB7E72 236 | 0D258FE90296 237 | 0E83A374B513 238 | 0F230695923F 239 | 0FFBF65B5A14 240 | 11428B5BCE06 241 | 11428B5BCE07 242 | 11428B5BCE08 243 | 11428B5BCE09 244 | 11428B5BCE0A 245 | 11428B5BCE0F 246 | 11496F97752A 247 | 123F8888F322 248 | 1322285230B8 249 | 1565A172770F 250 | 157B10D84C6B 251 | 157C9A513FA5 252 | 15CAFD6159F6 253 | 160A91D29A9C 254 | 16551D52FD20 255 | 167A1BE102E0 256 | 16DDCB6B3F24 257 | 1717E34A7A8A 258 | 17193709ADF4 259 | 185FA3438949 260 | 1877ED29435A 261 | 18971D893494 262 | 1AB23CD45EF6 263 | 1ACC3189578C 264 | 1F107328DC8D 265 | 1F1A0A111B5B 266 | 1F1FFE000000 267 | 2031D1E57A3B 268 | 204752454154 269 | 21A600056CB0 270 | 22729A9BD40F 271 | 2338B4913111 272 | 2548A443DF28 273 | 25D60050BF6E 274 | 26643965B16E 275 | 26973EA74321 276 | 27FBC86A00D0 277 | 2A2C13CC242A 278 | 2A6D9205E7CA 279 | 2CB1A90071C8 280 | 2DD39A54E1F3 281 | 2ED3B15E7C0F 282 | 2EF720F2AF76 283 | 2FC1F32F51B1 284 | 2FEAE851C199 285 | 3060206F5B0A 286 | 31646241686C 287 | 321958042333 288 | 321A695BD266 289 | 340E40F81CD8 290 | 345547514B4D 291 | 356D46474348 292 | 369A4663ACD2 293 | 36ABF5874ED7 294 | 374BF468607F 295 | 381ECE050FBD 296 | 386B4D634A65 297 | 38FCF33072E0 298 | 3A09594C8587 299 | 3B7E4FD575AD 300 | 3C5D1C2BCD18 301 | 3E84D2612E2A 302 | 3FA7217EC575 303 | 410B9B40B872 304 | 414C41524F4E 305 | 415A54454B4D 306 | 4186562A5BB2 307 | 424C41524F4E 308 | 425A73484166 309 | 436A46587552 310 | 447AB7FD5A6B 311 | 44DD5A385AAF 312 | 44F0B5FBE344 313 | 45635EF66EF3 314 | 476242304C53 315 | 484558414354 316 | 484944204953 317 | 484A57696F4A 318 | 48734389EDC3 319 | 48C739E21A04 320 | 49FAE4E3849F 321 | 4A6352684677 322 | 4C32BAF326E0 323 | 4C6B69723461 324 | 4C961F23E6BE 325 | 4D3248735131 326 | 4D5076656D58 327 | 4E32336C6E38 328 | 4E4175623670 329 | 4F9F59C9C875 330 | 509359F131B1 331 | 51044EFB5AAB 332 | 5106CA7E4A69 333 | 513C85D06CDE 334 | 52264716EFDE 335 | 536653644C65 336 | 53C11F90822A 337 | 543B01B27A95 338 | 5481986D2D62 339 | 5544564E6E67 340 | 564777315276 341 | 568C9083F71C 342 | 57734F6F6974 343 | 57784A533069 344 | 584F66326877 345 | 5A1B85FCE20A 346 | 5EC39B022F2B 347 | 623055724556 348 | 62387B8D250D 349 | 6245E47352E6 350 | 62CED42A6D87 351 | 62D0C424ED8E 352 | 62EFD80AB715 353 | 645A166B1EEB 354 | 649D2ABBBD20 355 | 666E564F4A44 356 | 668770666644 357 | 66B03ACA6EE9 358 | 66D2B7DC39EF 359 | 66F3ED00FED7 360 | 67546972BC69 361 | 675557ECC92E 362 | 686A736A356E 363 | 68D3F7307C89 364 | 69FB7B7CD8EE 365 | 6A1987C40A21 366 | 6A676C315142 367 | 6A696B646631 368 | 6B6579737472 369 | 6BC1E1AE547D 370 | 6C78928E1317 371 | 6C94E1CED026 372 | 6D44B5AAF464 373 | 6D4C5B3658D2 374 | 6D4E334B6C48 375 | 6DB17C16B35B 376 | 6F4B6D644178 377 | 6F506F493353 378 | 703140FD6D86 379 | 70564650584F 380 | 710732200D34 381 | 71F3A315AD26 382 | 744E326B3441 383 | 752FBB5B7B45 384 | 756EF55E2507 385 | 77494C526339 386 | 77646B633657 387 | 77A84170B574 388 | 79674F96C771 389 | 7B296C40C486 390 | 7B296F353C6B 391 | 7C335FB121B5 392 | 7F33625BC129 393 | 8553263F4FF0 394 | 8697389ACA26 395 | 86AFD95200F7 396 | 87DF99D496CB 397 | 8829DA9DAF76 398 | 89347350BD36 399 | 8AD5517B4B18 400 | 8E5D33A6ED51 401 | 8ED41E8B8056 402 | 8FA1D601D0A2 403 | 911E52FD7CE4 404 | 9189449EA24E 405 | 91CE16C07AC5 406 | 91F93A5564C9 407 | 925B158F796F 408 | 92EE4DC87191 409 | 932B9CB730EF 410 | 94414C1A07DC 411 | 95093F0B2E22 412 | 961C0DB4A7ED 413 | 987A7F7F1A35 414 | 9B832A9881FF 415 | 9CB290282F7D 416 | 9DC282D46217 417 | 9F42971E8322 418 | A10F303FC879 419 | A21680C27773 420 | A22AE129C013 421 | A2ABB693CE34 422 | A4F204203F56 423 | A56C2DF9A26D 424 | A57186BDD2B9 425 | A643F952EA57 426 | A6CAC2886412 427 | A7ABBC77CC9E 428 | A8D0D850A606 429 | A920F32FE93A 430 | AA0720018738 431 | AB4E7045E97D 432 | AC70CA327A04 433 | AD4FB33388BF 434 | AD9E0A1CA2F7 435 | AFD0BA94D624 436 | B1ACA33180A5 437 | B35A0E4ACC09 438 | B39AE17435DC 439 | B468D1991AF9 440 | B578F38A5C61 441 | B725F9CBF183 442 | B7BF0C13066E 443 | B8A1F613CF3D 444 | BA5B895DA162 445 | BBA840BA1C57 446 | BBE8FFFCF363 447 | BEDB604CC9D1 448 | BF1F4424AF76 449 | BFB6796A11DB 450 | BFC8E353AF63 451 | C0C1C2C3C4C5 452 | C0DECE673829 453 | C2B7EC7D4EB1 454 | C3C88C6340B8 455 | C3F19EC592A2 456 | C4104FA3C526 457 | C5132C8980BC 458 | C5CFE06D9EA3 459 | C620318EF179 460 | C6D375B99972 461 | C96BD1CE607F 462 | CB779C50E1BD 463 | CBA6AE869AD5 464 | CC6B3B3CD263 465 | D0D1D2D3D4D5 466 | D21762B2DE3B 467 | D2A597D76936 468 | D327083A60A7 469 | D4FE03CE5B06 470 | D4FE03CE5B07 471 | D4FE03CE5B08 472 | D4FE03CE5B09 473 | D4FE03CE5B0A 474 | D4FE03CE5B0F 475 | D58023BA2BDC 476 | D9A37831DCE5 477 | DB1A3338B2EB 478 | DD61EB6BCE22 479 | DF37DCB6AFB3 480 | E10623E7A016 481 | E241E8AFCBAF 482 | E2A5DC8E066F 483 | E4F65C0EF32C 484 | E55A3CA71826 485 | E64A986A5D94 486 | E703589DB50B 487 | EB0A8FF88ADE 488 | EC0A9B1A9E06 489 | ED646C83A4F3 490 | EE4CC572B40E 491 | EEB420209D0C 492 | F101622750B7 493 | F1B9F5669CC8 494 | F23442436765 495 | F238D78FF48F 496 | F26E21EDCEE2 497 | F4396E468114 498 | F4CD5D4C13FF 499 | F662248E7E89 500 | F72A29005459 501 | F792C4C76A5C 502 | F7A39753D018 503 | F9861526130F 504 | FAD63ECB5891 505 | 506 | # Some keys of https://w3bsit3-dns.com and https://ikey.ru 507 | BC4580B7F20B 508 | 8E26E45E7D65 509 | A7141147D430 510 | 18E3A02B5EFF 511 | E328A1C7156D 512 | 8A8D88151A00 513 | 7A86AA203788 514 | 72F96BDD3714 515 | 4B609876BBA3 516 | C76BF71A2509 517 | 1B61B2E78C75 518 | 045CECA15535 519 | 6B07877E2C5C 520 | 0CE7CD2CC72B 521 | 8C97CD7A0E56 522 | 3367BFAA91DB 523 | EA0FD73CB149 524 | B81F2B0C2F66 525 | BB52F8CCE07F 526 | 46D78E850A7E 527 | E4821A377B75 528 | 8791B2CCB5C4 529 | D5524F591EED 530 | BAFF3053B496 531 | 0F318130ED18 532 | 42E9B54E51AB 533 | 7413B599C4EA 534 | 9EA3387A63C1 535 | B27ADDFB64B0 536 | E56AC127DD45 537 | 0BE5FAC8B06A 538 | FD8705E721B0 539 | 7259FA0197C6 540 | 22052B480D11 541 | 9D993C5D4EF4 542 | C65D4EAA645B 543 | 0EB23CC8110B 544 | 3A8A139C20B4 545 | 19FC84A3784B 546 | 0F01CEFF2742 547 | A3FAA6DAFF67 548 | BC2D1791DEC1 549 | 7A396F0D633D 550 | ACFFFFFFFFFF 551 | B9F8A7D83978 552 | 77DABC9825E1 553 | 518DC6EEA089 554 | 044CE1872BC3 555 | 114D6BE9440C 556 | AFCEF64C9913 557 | 558 | # Russian Troika card 559 | 08B386463229 560 | 0E8F64340BA4 561 | 0F1C63013DBA 562 | 2AA05ED1856F 563 | 2B7F3253FAC5 564 | 69A32F1C2F19 565 | 73068F118C13 566 | 9BECDF3D9273 567 | A73F5DC1D333 568 | A82607B01C0D 569 | AE3D65A3DAD4 570 | CD4C61C26E3D 571 | D3EAFB5DF46D 572 | E35173494A81 573 | FBC2793D540B 574 | 5125974CD391 575 | ECF751084A80 576 | 7545DF809202 577 | AB16584C972A 578 | 7A38E3511A38 579 | C8454C154CB5 580 | 04C297B91308 581 | EFCB0E689DB3 582 | 07894FFEC1D6 583 | FBA88F109B32 584 | 2FE3CB83EA43 585 | B90DE525CEB6 586 | 1CC219E9FEC1 587 | A74332F74994 588 | 764CD061F1E6 589 | 8F79C4FD8A01 590 | CD64E567ABCD 591 | CE26ECB95252 592 | ABA208516740 593 | 9868925175BA 594 | 16A27AF45407 595 | 372CC880F216 596 | 3EBCE0925B2F 597 | 73E5B9D9D3A4 598 | 0DB520C78C1C 599 | 70D901648CB9 600 | C11F4597EFB5 601 | B39D19A280DF 602 | 403D706BA880 603 | 7038CD25C408 604 | 6B02733BB6EC 605 | EAAC88E5DC99 606 | 4ACEC1205D75 607 | 2910989B6880 608 | 31C7610DE3B0 609 | 5EFBAECEF46B 610 | F8493407799D 611 | 6B8BD9860763 612 | D3A297DC2698 613 | FBF225DC5D58 614 | 615 | # Keys from RfidResearchGroup proxmark3 project 616 | # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/default_keys.dic 617 | 0854BF31111E 618 | 0C03A720F208 619 | 135B88A94B8B 620 | 1FCEF3005BCF 621 | 2F47741062A0 622 | 361F69D2C462 623 | 3A09911D860C 624 | 3D50D902EA48 625 | 400BC9BE8976 626 | 4708111C8604 627 | 50A11381502C 628 | 560F7CFF2D81 629 | 60012E9BA3FA 630 | 6018522FAC02 631 | 66B31E64CA4B 632 | 6700F10FEC09 633 | 7A09CC1DB70A 634 | 81BFBE8CACBA 635 | 8A036920AC0C 636 | 8A19D40CF2B5 637 | 96A301BCE267 638 | 9E53491F685B 639 | A170D9B59F95 640 | AE8587108640 641 | B4166B0A27EA 642 | BB467463ACD6 643 | BFF123126C9B 644 | C01FC822C6E5 645 | D58660D1ACDE 646 | D80511FC2AB4 647 | D9BCDE7FC489 648 | DE1FCBEC764B 649 | E67C8010502D 650 | FF58BA1B4478 651 | -------------------------------------------------------------------------------- /dictionary_parser.l: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | %{ 21 | 22 | #include 23 | #include "dictionary.h" 24 | 25 | static uint8_t key_token[6]; 26 | 27 | typedef enum { 28 | DT_KEY = 1, 29 | DT_ERR = 2 30 | } dict_token_t; 31 | 32 | %} 33 | 34 | %option yylineno 35 | 36 | hex_digit [0-9a-fA-F] 37 | key {hex_digit}{12} 38 | comment #[^\n]* 39 | 40 | %% 41 | 42 | {key} { 43 | read_key(key_token, dp_text); 44 | return DT_KEY; 45 | } 46 | {comment} {} // eat comments 47 | [ \t\r\n]+ {} // eat white space 48 | . { 49 | printf("Line: %d - Unrecognized input: %s\n", dp_lineno, dp_text); 50 | return DT_ERR; 51 | } 52 | 53 | %% 54 | 55 | // Reset the line numbering at the end of a file 56 | int dp_wrap() { dp_lineno = 1; return 1; } 57 | 58 | // Parser loop. Import keys to the dictionary as they are found. 59 | int dictionary_import(FILE* input) { 60 | dict_token_t token; 61 | int res = 0; 62 | int keys_count = 0; 63 | int imported_count = 0; 64 | dp_in = input; 65 | 66 | while((token = dp_lex())) { 67 | switch(token) { 68 | case DT_KEY: 69 | ++keys_count; 70 | if (dictionary_add(key_token)) 71 | ++imported_count; 72 | break; 73 | 74 | case DT_ERR: 75 | default: 76 | res = -1; 77 | break; 78 | } 79 | } 80 | 81 | if (res) 82 | printf("There were errors.\n"); 83 | 84 | printf("%d keys (of %d) imported.\n", imported_count, keys_count); 85 | 86 | return res; 87 | } 88 | -------------------------------------------------------------------------------- /mac.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include "util.h" 23 | #include "mac.h" 24 | #include "tag.h" 25 | 26 | // The DES MAC key in use 27 | unsigned char current_mac_key[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 28 | 29 | 30 | /** 31 | * Compute a DES MAC, use DES in CBC mode. Key and output should be 8 32 | * bytes. The length specifies the length of the input in bytes. It 33 | * will be zero padded to 8 byte alignment if required. 34 | */ 35 | int compute_mac(const unsigned char* input, 36 | unsigned char* output, 37 | const unsigned char* key, 38 | long length) { 39 | 40 | DES_cblock des_key = 41 | { key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7] }; 42 | 43 | // todo zeropad input if required 44 | const unsigned char* padded_input = input; 45 | 46 | // Generate a key schedule. Don't be picky, allow bad keys. 47 | DES_key_schedule schedule; 48 | DES_set_key_unchecked(&des_key, &schedule); 49 | 50 | // IV is all zeroes 51 | unsigned char ivec[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 52 | 53 | // Compute the DES in CBC 54 | DES_ncbc_encrypt(padded_input, output, length, &schedule, &ivec, 1); 55 | 56 | // Move up and truncate (we only want 8 bytes) 57 | for (int i = 0; i < 8; ++i) 58 | output[i] = output[length - 8 + i]; 59 | for (int i = 8; i < length; ++i) 60 | output[i] = 0; 61 | 62 | return 0; 63 | } 64 | 65 | /** 66 | * Compute the MAC of a given block with the specified 8 byte 67 | * key. Return a 8 byte MAC value. 68 | * 69 | * The input to MAC algo [ 4 serial | 14 data | 6 0-pad ] 70 | * 71 | * If update is * nonzero, the mac of the current tag is updated. If 72 | * not, the MAC is simply printed. 73 | */ 74 | unsigned char* compute_block_mac(unsigned int block, 75 | const unsigned char* key, 76 | int update) { 77 | 78 | static unsigned char output[8]; 79 | 80 | // Input to MAC algo [ 4 serial | 14 data | 6 0-pad ] 81 | unsigned char input[24]; 82 | memcpy(&input, current_tag.amb[0].mbm.abtUID, 4); 83 | memcpy(&input[4], current_tag.amb[block].mbd.abtData, 14); 84 | memset(&input[18], 0, 6); 85 | 86 | int res = 0; 87 | res = compute_mac(input, output, key, 24); 88 | 89 | // Ret null on error 90 | if (res != 0) return NULL; 91 | 92 | // Should the new MAC be written back? 93 | if (update) { 94 | memcpy(¤t_tag.amb[block].mbd.abtData[14], output, 2); 95 | } 96 | 97 | return output; 98 | } 99 | -------------------------------------------------------------------------------- /mac.h: -------------------------------------------------------------------------------- 1 | #ifndef MAC__H 2 | #define MAC__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | */ 22 | 23 | // The DES MAC key in use 24 | extern unsigned char current_mac_key[]; 25 | 26 | 27 | /** 28 | * Compute a DES MAC, use DES in CBC mode. Key and output should be 8 29 | * bytes. The length specifies the length of the input in bytes. It 30 | * will be zero padded to 8 byte alignment if required. 31 | */ 32 | int compute_mac(const unsigned char* input, 33 | unsigned char* output, 34 | const unsigned char* key, 35 | long length); 36 | 37 | /** 38 | * Compute the MAC of a given block with the specified 8 byte 39 | * key. Return a 8 byte MAC value. 40 | * 41 | * The input to MAC algo [ 4 serial | 14 data | 6 0-pad ] 42 | * 43 | * If update is * nonzero, the mac of the current tag is updated. If 44 | * not, the MAC is simply printed. 45 | */ 46 | unsigned char* compute_block_mac(unsigned int block, 47 | const unsigned char* key, 48 | int update); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /mfc-spec.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Anders Sundman 2 | # 3 | # This file is part of rkfterm. 4 | # 5 | # rkfterm is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # rkfterm is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with rkfterm. If not, see . 17 | 18 | 19 | # About the specification language: 20 | # 21 | # The primitive types in the specification language are Byte and Bit 22 | # and arrays of such. All other types need to be defined. 23 | # 24 | # Every specification must include the root type '.' 25 | 26 | 27 | # The root type is specified by its type name '.' 28 | . { 29 | Sector0 sector_0 # The types usesed here are defined below 30 | Sector1k[15] sectors_1k 31 | Sector4k[12] sectors_4k 32 | } 33 | 34 | # The type Sector_0 represents the first sector. 35 | Sector0 { 36 | { # This is an anonymous inner type. The type doesn't have a name. 37 | Byte[4] uid # Unique ID 38 | Byte cd # Check Byte; uid bytes xor:ed with cb should be 0 39 | Bit[4] - # Not used. Use '-' as a name in theese cases 40 | Bit[4] sak 41 | Byte[2] atqa 42 | Byte[8] manufacturer_data 43 | } block_0 44 | Block[2] blocks 45 | SectorTrailer trailer 46 | } 47 | 48 | Sector1k { 49 | Block[3] blocks 50 | SectorTrailer trailer 51 | } 52 | 53 | Sector4k { 54 | Block[15] blocks 55 | SectorTrailer trailer 56 | } 57 | 58 | Block { 59 | Byte[16] data 60 | } 61 | 62 | SectorTrailer { 63 | Byte[6] key_a 64 | ACL acl 65 | Byte[1] - # Undefined 66 | Byte[6] key_b 67 | } 68 | 69 | # See p.12 of Mifare MF1 IC S50 Functional Specification, rev. 4.0 70 | # _b suffixed bits are inverted versions of non suffixed counterparts 71 | ACL { 72 | Bit c2x3_b # bit 7 73 | Bit c2x2_b 74 | Bit c2x1_b 75 | Bit c2x0_b # bit 4 76 | Bit c1x3_b # bit 3 77 | Bit c1x2_b 78 | Bit c1x1_b 79 | Bit c1x0_b # bit 0 80 | 81 | Bit c1x3 # bit 7 82 | Bit c1x2 83 | Bit c1x1 84 | Bit c1x0 # bit 4 85 | Bit c3x3_b # bit 3 86 | Bit c3x2_b 87 | Bit c3x1_b 88 | Bit c3x0_b # bit 0 89 | 90 | Bit c3x3 # bit 7 91 | Bit c3x2 92 | Bit c3x1 93 | Bit c3x0 # bit 4 94 | Bit c2x3 # bit 3 95 | Bit c2x2 96 | Bit c2x1 97 | Bit c2x0 # bit 0 98 | } 99 | -------------------------------------------------------------------------------- /mfterm.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011-2013 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | * 19 | * Parts of code used in this file are from the GNU readline library file 20 | * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "term_cmd.h" 31 | #include "mfterm.h" 32 | #include "util.h" 33 | #include "spec_syntax.h" 34 | 35 | #include "config.h" 36 | 37 | int stop_input_loop_ = 0; 38 | void stop_input_loop() { 39 | stop_input_loop_ = 1; 40 | } 41 | 42 | char* completion_cmd_generator(const char* text, int state); 43 | char* completion_sub_cmd_generator(const char* text, int state); 44 | char* completion_spec_generator(const char* text, int state); 45 | int perform_filename_completion(); 46 | char** mft_completion(char* text, int start, int end); 47 | int execute_line(char* line); 48 | void initialize_readline(); 49 | void parse_cmdline(int argc, char** argv); 50 | 51 | void print_help(); 52 | void print_version(); 53 | 54 | typedef char** rl_completion_func_t(const char*, int, int); 55 | 56 | int main(int argc, char** argv) { 57 | parse_cmdline(argc, argv); 58 | initialize_readline(); 59 | input_loop(); 60 | return 0; 61 | } 62 | 63 | void parse_cmdline(int argc, char** argv) { 64 | static struct option long_options[] = { 65 | {"help", no_argument, 0, 'h' }, 66 | {"version", no_argument, 0, 'v' }, 67 | {"tag", required_argument, 0, 't' }, 68 | {"keys", required_argument, 0, 'k' }, 69 | {"dict", required_argument, 0, 'd' }, 70 | {0, 0, 0, 0 } 71 | }; 72 | 73 | char* tag_file = NULL; 74 | char* keys_file = NULL; 75 | char* dict_file = NULL; 76 | 77 | int opt = 0; 78 | int long_index = 0; 79 | while ((opt = getopt_long(argc, argv,"hvt:k:d:", 80 | long_options, &long_index )) != -1) { 81 | switch (opt) { 82 | case 'h' : 83 | print_help(); 84 | exit(0); 85 | case 'v' : 86 | print_version(); 87 | exit(0); 88 | case 't' : tag_file = optarg; 89 | break; 90 | case 'k' : keys_file = optarg; 91 | break; 92 | case 'd' : dict_file = optarg; 93 | break; 94 | default : 95 | exit(1); 96 | } 97 | } 98 | 99 | // If a tag file was specified, load it 100 | if (tag_file != NULL && com_load_tag(tag_file)) 101 | exit(0); 102 | 103 | // If a keys file was specified, load it 104 | if (keys_file != NULL && com_keys_load(keys_file)) 105 | exit(0); 106 | 107 | // If a dictionary file was specified, load it 108 | if (dict_file != NULL && com_dict_load(dict_file)) 109 | exit(0); 110 | 111 | // Default is to do nothing, just enter the terminal 112 | } 113 | 114 | // Request user input until stop_intput_loop_ == 0 115 | void input_loop() { 116 | char *line, *s; 117 | while (stop_input_loop_ == 0) { 118 | line = readline ("$ "); 119 | 120 | if (!line) 121 | break; 122 | 123 | s = trim(line); 124 | 125 | if (*s) { 126 | add_history(s); 127 | execute_line(s); 128 | } 129 | free (line); 130 | } 131 | } 132 | 133 | /* Execute a command line. */ 134 | int execute_line (char* line) { 135 | if (strncmp(line, ".", 1) == 0) 136 | return exec_path_command(line); 137 | 138 | command_t* command = find_command(line); 139 | 140 | if (!command) { 141 | fprintf (stderr, "%s: No such command.\n", line); 142 | return -1; 143 | } 144 | 145 | // Skip past command and ws to the arguments 146 | line += strlen(command->name); 147 | line = trim(line); 148 | 149 | return (*(command->func))(line); 150 | } 151 | 152 | void initialize_readline() 153 | { 154 | rl_readline_name = "MFT"; 155 | rl_attempted_completion_function = (rl_completion_func_t*)mft_completion; 156 | } 157 | 158 | /* Attempt to complete on the contents of TEXT. START and END bound the 159 | region of rl_line_buffer that contains the word to complete. TEXT is 160 | the word to complete. We can use the entire contents of rl_line_buffer 161 | in case we want to do some simple parsing. Return the array of matches, 162 | or NULL if there aren't any. */ 163 | char** mft_completion(char* text, int start, int end) { 164 | char **matches = (char **)NULL; 165 | 166 | // Don't complete on files for most cases 167 | rl_attempted_completion_over = 1; 168 | 169 | // Add the trailing space unless told otherwise 170 | #ifdef HAVE_RL_COMPLETION_SUPPRESS_APPEND 171 | rl_completion_suppress_append = 0; 172 | #endif 173 | 174 | // Complete strings starting with '.' as specification paths 175 | if (text[0] == '.' && instance_root) { 176 | #ifdef HAVE_RL_COMPLETION_SUPPRESS_APPEND 177 | rl_completion_suppress_append = 1; // no trailing space on paths 178 | #endif 179 | return rl_completion_matches(text, completion_spec_generator); 180 | } 181 | 182 | // Commands start at 0 183 | if (start == 0) 184 | return rl_completion_matches(text, completion_cmd_generator); 185 | 186 | // else: Sub commands and file arguments start at > 0 187 | 188 | // Try to match sub commands 189 | matches = rl_completion_matches(text, completion_sub_cmd_generator); 190 | if (matches) 191 | return matches; 192 | 193 | if (perform_filename_completion()) { 194 | // Do complete on filenames 195 | rl_attempted_completion_over = 0; 196 | return matches; 197 | } 198 | 199 | return matches; 200 | } 201 | 202 | 203 | int perform_filename_completion() { 204 | for (int i = 0; commands[i].name; ++i) { 205 | if (commands[i].fn_arg && 206 | strncmp(rl_line_buffer, commands[i].name, 207 | strlen(commands[i].name)) == 0) { 208 | return 1; 209 | } 210 | } 211 | return 0; 212 | } 213 | 214 | /** 215 | Called to generate completion suggestions. 216 | state == 0 on first call. 217 | */ 218 | char* completion_cmd_generator(const char* text, int state) { 219 | static int cmd_index; 220 | static size_t len; 221 | 222 | // First call? 223 | if (!state) { 224 | cmd_index = 0; 225 | len = strlen(text); // Cached for performance 226 | } 227 | 228 | // Return next suggestion 229 | char *name; 230 | while ((name = commands[cmd_index].name)) { 231 | ++cmd_index; 232 | 233 | // Check if the command is applicable 234 | if (strncmp(name, text, len) == 0) { 235 | char* r = malloc(strlen(name) + 1); 236 | strcpy(r, name); 237 | return r; 238 | } 239 | } 240 | 241 | // No (more) matches 242 | return (char*) NULL; 243 | } 244 | 245 | char* completion_sub_cmd_generator(const char* text, int state) { 246 | static int cmd_index; 247 | static size_t len, full_len; 248 | 249 | // First call? 250 | if (!state) { 251 | cmd_index = 0; 252 | len = strlen(text); // Cached for performance 253 | full_len = strlen(rl_line_buffer); 254 | } 255 | 256 | // Return next suggestion 257 | char* name; 258 | while ((name = commands[cmd_index].name)) { 259 | ++cmd_index; 260 | 261 | // Extract command and sub-command 262 | char buff[128]; 263 | strncpy(buff, name, sizeof(buff)); 264 | char* cmd = strtok(buff, " "); 265 | char* sub = strtok(NULL, " "); 266 | 267 | // Make sure the command *has* a sub command 268 | // and that we have the right command. 269 | if (cmd && sub && strncmp(rl_line_buffer, name, full_len) == 0) { 270 | // Check if the sub command is applicable 271 | if (strncmp(sub, text, len) == 0) { 272 | char* r = malloc(strlen(sub) + 1); 273 | strcpy(r, sub); 274 | return r; 275 | } 276 | } 277 | } 278 | 279 | // No (more) matches 280 | return (char*) NULL; 281 | } 282 | 283 | /** 284 | * Called to generate completion suggestions. 285 | * state == 0 on first call. 286 | */ 287 | char* completion_spec_generator(const char* text, int state) { 288 | 289 | // Parent context is initialized on the first call 290 | static instance_t* parent_inst; 291 | static const char* parent_end; 292 | static size_t parent_end_len; 293 | 294 | // Instace iter is advanced on each repeated call 295 | static instance_list_t* inst_iter; 296 | 297 | // First call? 298 | if (!state) { 299 | 300 | // Set the parent context 301 | if (parse_partial_spec_path(text, &parent_end, &parent_inst) != 0) 302 | return NULL; // on error 303 | 304 | parent_end_len = strlen(parent_end); 305 | 306 | // The instance iter points to the first fields 307 | inst_iter = parent_inst->fields; 308 | } 309 | 310 | while (inst_iter) { 311 | instance_t* inst = inst_iter->instance; 312 | inst_iter = inst_iter->next_; 313 | 314 | // Anonymous filler field 315 | if (inst->field->name == NULL) 316 | continue; 317 | 318 | char* fname = inst->field->name; 319 | size_t fname_len = strlen(fname); 320 | 321 | // Check if the field is applicable - right prefix 322 | if (fname_len >= parent_end_len && 323 | strncmp(fname, parent_end, parent_end_len) == 0) { 324 | 325 | if (parent_end - text <= 0) 326 | return NULL; 327 | size_t parent_len = (size_t)(parent_end - text); 328 | char* str = malloc(parent_len + fname_len + 1); 329 | 330 | // The parent part ending with '.' 331 | strncpy(str, text, parent_len); 332 | 333 | // The field 334 | strncpy(str + parent_len, fname, fname_len); 335 | 336 | // Null termination 337 | *(str + parent_len + fname_len) = '\0'; 338 | 339 | return str; 340 | } 341 | } 342 | 343 | // No (more) matches 344 | return NULL; 345 | } 346 | 347 | void print_help() { 348 | printf("A terminal interface for working with Mifare Classic tags.\n"); 349 | printf("Usage: mfterm [-v] [-h] [-k keyfile]\n"); 350 | printf("\n"); 351 | printf("Options: \n"); 352 | printf(" --help (-h) Show this help message.\n"); 353 | printf(" --version (-v) Display version information.\n"); 354 | printf(" --tag=tagfile (-t) Load a tag from the specified file.\n"); 355 | printf(" --keys=keyfile (-k) Load keys from the specified file.\n"); 356 | printf(" --dict=dictfile (-d) Load dictionary from the specified file.\n"); 357 | printf("\n"); 358 | printf("Report bugs to: anders@4zm.org\n"); 359 | printf(PACKAGE_NAME); printf(" home page: \n"); 360 | } 361 | 362 | void print_version() { 363 | printf(PACKAGE_STRING); printf("\n"); 364 | printf("Copyright (C) 2011-2013 Anders Sundman \n"); 365 | printf("License GPLv3+: GNU GPL version 3 or later \n"); 366 | printf("This is free software: you are free to change and redistribute it.\n"); 367 | printf("There is NO WARRANTY, to the extent permitted by law.\n"); 368 | } 369 | -------------------------------------------------------------------------------- /mfterm.h: -------------------------------------------------------------------------------- 1 | #ifndef MFTERM__H 2 | #define MFTERM__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | */ 22 | 23 | // The main loop of mfterm 24 | void input_loop(); 25 | 26 | // Called to exit the mfterm program 27 | void stop_input_loop(); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /mfterm.man: -------------------------------------------------------------------------------- 1 | .\" mfterm Manual 2 | .\" Contact anders@4zm.org to correct errors or typos. 3 | 4 | .TH mfterm 1 "14 Sep 2016" "1.0.7" "mfterm Manual" 5 | 6 | .SH NAME 7 | mfterm \- The Mifare Terminal 8 | 9 | .SH SYNOPSIS 10 | .B mfterm 11 | [\fB-v\fR\] 12 | [\fB-h\fR\] 13 | [\fB-t\fR \fItagfile\fR] 14 | [\fB-k\fR \fIkeyfile\fR] 15 | [\fB-d\fR \fIdictionary\fR] 16 | 17 | 18 | .\" ---------------------- DESCRIPTION ----------------------------- 19 | 20 | 21 | .SH DESCRIPTION 22 | 23 | .P 24 | 25 | mfterm is a terminal interface for working with Mifare tags. 26 | 27 | .PP 28 | 29 | The program is used as an interactive shell to read and write Mifare 30 | tags using libnfc and a libnfc compatible reader or to simply 31 | manipulate Mifare data dumps from files. See the \fBCOMMANDS\fR 32 | section below for a description of the commands available. 33 | 34 | .PP 35 | 36 | In mfterm, there are a number of global state variables. One for tag 37 | data, one for keys and some others. Data is read and loaded to this 38 | memory and written and saved from the same. The contents of the tag 39 | data variable is displayed using the \fBprint\fR command. The keys in 40 | the key variable are displayed using the \fBkeys\fR command. Both tag 41 | and key variables are 4k, but only the first 1k is used for 1k tags. 42 | 43 | .PP 44 | 45 | Please see the \fBREADME\fR and \fBINSTALL\fR files for further 46 | information. 47 | 48 | 49 | .\" --------------------------- OPTIONS --------------------------- 50 | 51 | 52 | .SH OPTIONS 53 | These are the command line options of mfterm. 54 | 55 | .TP 5 56 | 57 | .TP 58 | \fB-h\fR, \fB--help\fR 59 | Displays a help message. 60 | 61 | .TP 62 | \fB-v\fR, \fB--version\fR 63 | Display version information. 64 | 65 | .TP 66 | \fB-t\fR \fItagfile\fR, \fB--tag=\fR\fItagfile\fR 67 | Load a tag from the specified file. Before starting the terminal. 68 | 69 | .TP 70 | \fB-k\fR \fIkeyfile\fR, \fB--keys=\fR\fIkeyfile\fR 71 | Load keys from the specified file. Before starting the terminal. 72 | 73 | .TP 74 | \fB-d\fR \fIdictionary\fR, \fB--dict=\fR\fIdictionary\fR 75 | Load dictionary from the specified file. Before starting the terminal. 76 | 77 | 78 | .\" --------------------------- COMMANDS --------------------------- 79 | 80 | 81 | .SH COMMANDS 82 | These are the commands available from the mfterm prompt. 83 | 84 | 85 | .\" --------------------- TAG - COMMANDS --------------------------- 86 | 87 | 88 | .RS -4 89 | .B Tag Commands: 90 | .RE 91 | 92 | .TP 93 | \fBprint \fR[\fB1k\fR|\fB4k\fR] 94 | Print the current tag data. The data is formatted to show sectors and 95 | blocks in hexadecimal. Optionally specify tag size (default is 1k). 96 | 97 | .TP 98 | \fBread \fR[\fBA\fR|\fBB\fR] 99 | Read a tag. A libnfc compatible reader must be connected and a tag 100 | present. The keys in the key state variable will be used to 101 | authenticate each sector. Optionally specify witch key to use for 102 | reading (default is A). 103 | 104 | .TP 105 | \fBwrite \fR[\fBA\fR|\fBB\fR] 106 | Write a tag. A libnfc compatible reader must be connected and a tag 107 | present. The keys in the key state variable will be used to 108 | authenticate each sector. Optionally specify witch key to use for 109 | reading (default is A). 110 | 111 | .TP 112 | \fBload\fR 113 | Load tag data from a file. The file should be a raw binary file 114 | containing exactly 4k. If the tag data represents a 1k tag, the data 115 | should be padded. 116 | 117 | .TP 118 | \fBsave\fR 119 | Save tag data to a file. A raw binary dump of the data will be 120 | written. If the tag is a 1k tag, the data will be padded with zeroes 121 | to 4k size. 122 | 123 | .TP 124 | \fBclear\fR 125 | Clear the current tag data in memory. 126 | 127 | .TP 128 | \fBprint keys \fR[\fB1k\fR|\fB4k\fR] 129 | Extract the key information from the tag loaded into memory and 130 | display it. This is not the same as the keys command. The later will 131 | print the keys stored in the keys variable, this prints keys from the 132 | tag. 133 | 134 | .TP 135 | \fBprint ac\fR 136 | Print the access conditions for each block. Possible values are A, B, 137 | A|B or '-'. Their meanings are, in turn, that the A or B or both A and 138 | B keys or neither key can be used. The columns R, W, I, D represents 139 | read, write, increment and decrement. They apply for all non trailer 140 | blocks. For the trailer blocks the columns AR, AW, ACR, ACW, BR, BW 141 | apply. They are permissions for; reading the A-key, writing the A-key, 142 | reading the access control bits, writing the access control bits, 143 | reading the B-key and writing the B-key. 144 | 145 | .TP 146 | \fBset \fIblock offset\fR \fB=\fR \fIxx xx xx\fR 147 | Write some values to the tag variable in memory. Specify data as 148 | hexadecimal bytes separated by spaces. 149 | 150 | 151 | .\" --------------------- KEY - COMMANDS --------------------------- 152 | 153 | 154 | .RS -4 155 | .B Key Management Commands: 156 | .RE 157 | 158 | .TP 159 | \fBkeys\fR [\fB1k\fR|\fB4k\fR] 160 | Print the keys currently loaded. Optionally specify if keys for the 161 | full 4k tag should be displayed or just the ones for the first 162 | 1k. Default is 1k. 163 | 164 | .TP 165 | \fBkeys load\fR \fIfile\fR 166 | Load keys from a file into memory. The key file is a regular binary 167 | tag dump, but only the key fields are used. That means that any tag 168 | dump can be loaded as keys. 169 | 170 | .TP 171 | \fBkeys save\fR \fIfile\fR 172 | Save the current keys in memory to a file. The keys will be saved as a 173 | normal binary tag dump with all values except the keys cleared. 174 | 175 | .TP 176 | \fBkeys import\fR 177 | Import keys from the current tag. 178 | 179 | .TP 180 | \fBkeys clear\fR 181 | Clear the keys in memory. 182 | 183 | .TP 184 | \fBkeys set\fR \fBA\fR|\fBB\fR \fIsector\fR \fIkey\fR 185 | Set a specific key explicitly. Specify the key in hex, if it is an A- 186 | or B-key and what sector to set the key for. 187 | 188 | .TP 189 | \fBkeys test\fR 190 | Try to authenticate with the keys. Use this command to test a set of 191 | keys with a specific tag. 192 | 193 | .\" ------------------ PIRATE - COMMANDS --------------------------- 194 | 195 | .RS -4 196 | .B Pirate Card Commands: 197 | .RE 198 | 199 | These commands will only work on the back door:ed pirate cards (aka 200 | Chinese magic cards) with writable first block. 201 | 202 | .TP 203 | \fBread unlocked\fR 204 | Read the card without using keys and disregard access control bits. 205 | 206 | .TP 207 | \fBwrite unlocked\fR 208 | Write to a back door:ed 1k tag. This will write block 0 and possibly 209 | modify the UID. 210 | 211 | .\" -------------------- DICT - COMMANDS --------------------------- 212 | 213 | .RS -4 214 | .B Dictionary Attack Commands: 215 | .RE 216 | 217 | .TP 218 | \fBdict load\fR \fIfile\fR 219 | Load a dictionary key file. This is a regular text file with one key 220 | written in hex per line. Loading multiple dictionaries will merge 221 | their contents and remove duplicates. 222 | 223 | .TP 224 | \fBdict clear\fR 225 | Clear the key dictionary in memory. 226 | 227 | .TP 228 | \fBdict attack\fR 229 | Find keys of a physical tag by trying all keys in the loaded 230 | dictionary. If any keys are found the current keys variable will be 231 | updated. 232 | 233 | .TP 234 | \fBdict\fR 235 | Print the contents of the key dictionary currently loaded. 236 | 237 | .\" -------------------- SPEC - COMMANDS --------------------------- 238 | 239 | .RS -4 240 | .B Contents Specification Commands: 241 | .RE 242 | 243 | .TP 244 | \fBspec load\fR \fIfile\fR 245 | Load a specification file. 246 | 247 | .TP 248 | \fBspec clear\fR 249 | Unload the specification. 250 | 251 | .TP 252 | \fBspec\fR 253 | Print the specification. 254 | 255 | .\" --------------------- MAC - COMMANDS --------------------------- 256 | 257 | .RS -4 258 | .B MAC Commands: 259 | .RE 260 | 261 | These are commands for creating and validating DES MACs (message 262 | authentication codes) to sign the contents of specific blocks. 263 | 264 | .TP 265 | \fBmac key\fR [\fIkey\fR] 266 | Get or set MAC key. 267 | 268 | .TP 269 | \fBmac compute\fR \fI#block\fR 270 | Compute the MAC for a specified block. 271 | 272 | .TP 273 | \fBmac update\fR \fI#block\fR 274 | Compute the MAC for a specified block, truncate it and write it back 275 | into the current tag data. 276 | 277 | .TP 278 | \fBmac validate\fR [\fB1k\fR|\fB4k\fR] 279 | Validates MACs for every block of the tag. 280 | 281 | .\" -------------------- MISC - COMMANDS --------------------------- 282 | 283 | .RS -4 284 | .B General commands: 285 | .RE 286 | 287 | .TP 5 288 | 289 | .TP 290 | \fBquit\fR 291 | Exit the program. 292 | 293 | .TP 294 | \fBhelp\fR 295 | Show a list of available commands and a short description of each. 296 | 297 | 298 | .\" ------------------------- NOTES ------------------------------- 299 | 300 | 301 | .SH NOTE 302 | 303 | The \fBmac\fR and \fBspec\fR command groups are experimental. They 304 | 305 | 306 | .\" ---------------------- TRAILER STUFF --------------------------- 307 | 308 | 309 | .SH SEE ALSO 310 | nfc-list(1) 311 | 312 | .SH LICENSE 313 | Copyright (C) 2011-2016 Anders Sundman 314 | 315 | License GPLv3+: GNU GPL version 3 or later. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. 316 | 317 | .SH AUTHOR 318 | Anders Sundman 319 | -------------------------------------------------------------------------------- /mifare.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Public platform independent Near Field Communication (NFC) library examples 3 | * 4 | * Copyright (C) 2009 Roel Verdult 5 | * Copyright (C) 2010 Romain Tartière 6 | * Copyright (C) 2010, 2011 Romuald Conty 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 1) Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 2 )Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Note that this license only applies on the examples, NFC library itself is under LGPL 29 | * 30 | */ 31 | /** 32 | * @file mifare.c 33 | * @brief provide samples structs and functions to manipulate MIFARE Classic and Ultralight tags using libnfc 34 | */ 35 | #include "mifare.h" 36 | 37 | #include 38 | 39 | #include 40 | 41 | /** 42 | * @brief Execute a MIFARE Classic Command 43 | * @return Returns true if action was successfully performed; otherwise returns false. 44 | * @param pmp Some commands need additional information. This information should be supplied in the mifare_param union. 45 | * 46 | * The specified MIFARE command will be executed on the tag. There are different commands possible, they all require the destination block number. 47 | * @note There are three different types of information (Authenticate, Data and Value). 48 | * 49 | * First an authentication must take place using Key A or B. It requires a 48 bit Key (6 bytes) and the UID. 50 | * They are both used to initialize the internal cipher-state of the PN53X chip. 51 | * After a successful authentication it will be possible to execute other commands (e.g. Read/Write). 52 | * The MIFARE Classic Specification (http://www.nxp.com/acrobat/other/identification/M001053_MF1ICS50_rev5_3.pdf) explains more about this process. 53 | */ 54 | bool 55 | nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp) 56 | { 57 | uint8_t abtRx[265]; 58 | size_t szParamLen; 59 | uint8_t abtCmd[265]; 60 | //bool bEasyFraming; 61 | 62 | abtCmd[0] = mc; // The MIFARE Classic command 63 | abtCmd[1] = ui8Block; // The block address (1K=0x00..0x39, 4K=0x00..0xff) 64 | 65 | switch (mc) { 66 | // Read and store command have no parameter 67 | case MC_READ: 68 | case MC_STORE: 69 | szParamLen = 0; 70 | break; 71 | 72 | // Authenticate command 73 | case MC_AUTH_A: 74 | case MC_AUTH_B: 75 | szParamLen = sizeof(struct mifare_param_auth); 76 | break; 77 | 78 | // Data command 79 | case MC_WRITE: 80 | szParamLen = sizeof(struct mifare_param_data); 81 | break; 82 | 83 | // Value command 84 | case MC_DECREMENT: 85 | case MC_INCREMENT: 86 | case MC_TRANSFER: 87 | szParamLen = sizeof(struct mifare_param_value); 88 | break; 89 | 90 | // Please fix your code, you never should reach this statement 91 | default: 92 | return false; 93 | break; 94 | } 95 | 96 | // When available, copy the parameter bytes 97 | if (szParamLen) 98 | memcpy(abtCmd + 2, (uint8_t *) pmp, szParamLen); 99 | 100 | // FIXME: Save and restore bEasyFraming 101 | // bEasyFraming = nfc_device_get_property_bool (pnd, NP_EASY_FRAMING, &bEasyFraming); 102 | if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) { 103 | nfc_perror(pnd, "nfc_device_set_property_bool"); 104 | return false; 105 | } 106 | // Fire the mifare command 107 | int res; 108 | if ((res = nfc_initiator_transceive_bytes(pnd, abtCmd, 2 + szParamLen, abtRx, sizeof(abtRx), -1)) < 0) { 109 | if (res == NFC_ERFTRANS) { 110 | // "Invalid received frame", usual means we are 111 | // authenticated on a sector but the requested MIFARE cmd (read, write) 112 | // is not permitted by current acces bytes; 113 | // So there is nothing to do here. 114 | } 115 | else if (res == NFC_EMFCAUTHFAIL) { 116 | // Since we implement a dictionary brute force attack, 117 | // don't print an error on failed authentications. 118 | } 119 | else { 120 | nfc_perror(pnd, "nfc_initiator_transceive_bytes"); 121 | } 122 | // XXX nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, bEasyFraming); 123 | return false; 124 | } 125 | /* XXX 126 | if (nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, bEasyFraming) < 0) { 127 | nfc_perror (pnd, "nfc_device_set_property_bool"); 128 | return false; 129 | } 130 | */ 131 | 132 | // When we have executed a read command, copy the received bytes into the param 133 | if (mc == MC_READ) { 134 | if (res == 16) { 135 | memcpy(pmp->mpd.abtData, abtRx, 16); 136 | } else { 137 | return false; 138 | } 139 | } 140 | // Command succesfully executed 141 | return true; 142 | } 143 | -------------------------------------------------------------------------------- /mifare.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Public platform independent Near Field Communication (NFC) library examples 3 | * 4 | * Copyright (C) 2009 Roel Verdult 5 | * Copyright (C) 2010 Romain Tartière 6 | * Copyright (C) 2010, 2011 Romuald Conty 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 1) Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 2 )Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Note that this license only applies on the examples, NFC library itself is under LGPL 29 | * 30 | */ 31 | 32 | /** 33 | * @file mifare.h 34 | * @brief provide samples structs and functions to manipulate MIFARE Classic and Ultralight tags using libnfc 35 | */ 36 | 37 | #ifndef _LIBNFC_MIFARE_H_ 38 | # define _LIBNFC_MIFARE_H_ 39 | 40 | # include 41 | 42 | // Compiler directive, set struct alignment to 1 uint8_t for compatibility 43 | # pragma pack(1) 44 | 45 | typedef enum { 46 | MC_AUTH_A = 0x60, 47 | MC_AUTH_B = 0x61, 48 | MC_READ = 0x30, 49 | MC_WRITE = 0xA0, 50 | MC_TRANSFER = 0xB0, 51 | MC_DECREMENT = 0xC0, 52 | MC_INCREMENT = 0xC1, 53 | MC_STORE = 0xC2 54 | } mifare_cmd; 55 | 56 | // MIFARE command params 57 | struct mifare_param_auth { 58 | uint8_t abtKey[6]; 59 | uint8_t abtAuthUid[4]; 60 | }; 61 | 62 | struct mifare_param_data { 63 | uint8_t abtData[16]; 64 | }; 65 | 66 | struct mifare_param_value { 67 | uint8_t abtValue[4]; 68 | }; 69 | 70 | typedef union { 71 | struct mifare_param_auth mpa; 72 | struct mifare_param_data mpd; 73 | struct mifare_param_value mpv; 74 | } mifare_param; 75 | 76 | // Reset struct alignment to default 77 | # pragma pack() 78 | 79 | bool nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp); 80 | 81 | // Compiler directive, set struct alignment to 1 uint8_t for compatibility 82 | # pragma pack(1) 83 | 84 | // MIFARE Classic 85 | typedef struct { 86 | uint8_t abtUID[4]; 87 | uint8_t btBCC; 88 | uint8_t btUnknown; 89 | uint8_t abtATQA[2]; 90 | uint8_t abtUnknown[8]; 91 | } mifare_classic_block_manufacturer; 92 | 93 | typedef struct { 94 | uint8_t abtData[16]; 95 | } mifare_classic_block_data; 96 | 97 | typedef struct { 98 | uint8_t abtKeyA[6]; 99 | uint8_t abtAccessBits[4]; 100 | uint8_t abtKeyB[6]; 101 | } mifare_classic_block_trailer; 102 | 103 | typedef union { 104 | mifare_classic_block_manufacturer mbm; 105 | mifare_classic_block_data mbd; 106 | mifare_classic_block_trailer mbt; 107 | } mifare_classic_block; 108 | 109 | typedef struct { 110 | mifare_classic_block amb[256]; 111 | } mifare_classic_tag; 112 | 113 | // MIFARE Ultralight 114 | typedef struct { 115 | uint8_t sn0[3]; 116 | uint8_t btBCC0; 117 | uint8_t sn1[4]; 118 | uint8_t btBCC1; 119 | uint8_t internal; 120 | uint8_t lock[2]; 121 | uint8_t otp[4]; 122 | } mifareul_block_manufacturer; 123 | 124 | typedef struct { 125 | uint8_t abtData[16]; 126 | } mifareul_block_data; 127 | 128 | typedef union { 129 | mifareul_block_manufacturer mbm; 130 | mifareul_block_data mbd; 131 | } mifareul_block; 132 | 133 | typedef struct { 134 | mifareul_block amb[4]; 135 | } mifareul_tag; 136 | 137 | // Reset struct alignment to default 138 | # pragma pack() 139 | 140 | #endif // _LIBNFC_MIFARE_H_ 141 | -------------------------------------------------------------------------------- /mifare_ctrl.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | * 19 | * Parts of code used in this file are based on the Public platform 20 | * independent Near Field Communication (NFC) library example 21 | * nfc-mfclassic.c. It is thus covered by that license as well: 22 | * 23 | * Copyright (C) 2009, Roel Verdult 24 | * Copyright (C) 2010, Romuald Conty, Romain Tartière 25 | * Copyright (C) 2011, Adam Laurie 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that the following conditions are met: 29 | * 1) Redistributions of source code must retain the above copyright notice, 30 | * this list of conditions and the following disclaimer. 31 | * 2 )Redistributions in binary form must reproduce the above copyright 32 | * notice, this list of conditions and the following disclaimer in the 33 | * documentation and/or other materials provided with the distribution. 34 | * 35 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 36 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 39 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 40 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 41 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 42 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 43 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 44 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 45 | * POSSIBILITY OF SUCH DAMAGE. 46 | */ 47 | 48 | #include 49 | #include 50 | #include 51 | #include "mifare.h" 52 | #include "tag.h" 53 | #include "mifare_ctrl.h" 54 | 55 | // State of the device/tag - should be NULL between high level calls. 56 | static nfc_device* device = NULL; 57 | static nfc_target target; 58 | static mf_size_t size; 59 | static nfc_context* context; 60 | 61 | static const nfc_modulation mf_nfc_modulation = { 62 | .nmt = NMT_ISO14443A, 63 | .nbr = NBR_106, 64 | }; 65 | 66 | // Buffers used for raw bit/byte writes 67 | #define MAX_FRAME_LEN 264 68 | static uint8_t abtRx[MAX_FRAME_LEN]; 69 | static int szRxBits; 70 | 71 | 72 | int mf_connect(); 73 | int mf_disconnect(int ret_state); 74 | 75 | bool mf_configure_device(); 76 | bool mf_select_target(); 77 | 78 | bool mf_authenticate(size_t block, 79 | const uint8_t* key, 80 | mf_key_type_t key_type); 81 | 82 | bool mf_unlock(); 83 | 84 | bool mf_read_tag_internal(mf_tag_t* tag, 85 | const mf_tag_t* keys, 86 | mf_key_type_t key_type); 87 | 88 | bool mf_write_tag_internal(const mf_tag_t* tag, 89 | const mf_tag_t* keys, 90 | mf_key_type_t key_type); 91 | 92 | bool mf_dictionary_attack_internal(mf_tag_t* tag); 93 | 94 | bool mf_test_auth_internal(const mf_tag_t* keys, 95 | mf_size_t size, 96 | mf_key_type_t key_type); 97 | 98 | 99 | bool transmit_bits(const uint8_t *pbtTx, const size_t szTxBits); 100 | bool transmit_bytes(const uint8_t *pbtTx, const size_t szTx); 101 | 102 | int mf_disconnect(int ret_state) { 103 | nfc_close(device); 104 | nfc_exit(context); 105 | device = NULL; 106 | memset(&target, 0, sizeof(target)); 107 | return ret_state; 108 | } 109 | 110 | int mf_connect() { 111 | 112 | // Initialize libnfc and set the nfc_context 113 | nfc_init(&context); 114 | 115 | // Connect to (any) NFC reader 116 | device = nfc_open(context, NULL); 117 | if (device == NULL) { 118 | printf ("Could not connect to any NFC device\n"); 119 | return -1; // Don't jump here, since we don't need to disconnect 120 | } 121 | 122 | // Initialize the device as a reader 123 | if (!mf_configure_device()) { 124 | printf("Error initializing NFC device\n"); 125 | return mf_disconnect(-1); 126 | } 127 | 128 | // Try to find a tag 129 | if (!mf_select_target() || target.nti.nai.btSak == 0) { 130 | printf("Connected to device, but no tag found.\n"); 131 | return mf_disconnect(-1); 132 | } 133 | 134 | // Allow SAK & ATQA == 0. Assume 1k pirate card. 135 | if (target.nti.nai.btSak == 0 && target.nti.nai.abtAtqa[1] == 0) { 136 | size = MF_1K; 137 | return 0; 138 | } 139 | 140 | // Test if we are dealing with a Mifare Classic compatible tag 141 | if ((target.nti.nai.btSak & 0x08) == 0) { 142 | printf("Incompatible tag type: 0x%02x (i.e. not Mifare Classic).\n", 143 | target.nti.nai.btSak); 144 | return mf_disconnect(-1); 145 | } 146 | 147 | // Guessing tag size 148 | if ((target.nti.nai.abtAtqa[1] & 0x02)) { // 4K 149 | size = MF_4K; 150 | } 151 | else if ((target.nti.nai.abtAtqa[1] & 0x04)) { // 1K 152 | size = MF_1K; 153 | } 154 | else { 155 | printf("Unsupported tag size. ATQA 0x%02x 0x%02x (i.e. not [1|4]K.)\n", 156 | target.nti.nai.abtAtqa[0], target.nti.nai.abtAtqa[1]); 157 | return mf_disconnect(-1); 158 | } 159 | 160 | return 0; // Indicate success - we are now connected 161 | } 162 | 163 | 164 | int mf_read_tag(mf_tag_t* tag, mf_key_type_t key_type) { 165 | 166 | if (mf_connect()) 167 | return -1; // No need to disconnect here 168 | 169 | if (key_type == MF_KEY_UNLOCKED) { 170 | if (!mf_unlock()) { 171 | printf("Unlocked read requested, but unlock failed!\n"); 172 | return false; 173 | } 174 | } 175 | 176 | if (!mf_read_tag_internal(tag, ¤t_auth, key_type)) { 177 | printf("Read failed!\n"); 178 | return mf_disconnect(-1); 179 | } 180 | 181 | // Print the type of card 182 | if (target.nti.nai.btSak == 0x08 && 183 | target.nti.nai.abtAtqa[0] == 0x00 && target.nti.nai.abtAtqa[1] == 0x04) { 184 | printf("Read MIFARE Classic 1k (SAK: 08, ATQA: 00 04)\n"); 185 | } 186 | else if (target.nti.nai.btSak == 0x18 && 187 | target.nti.nai.abtAtqa[0] == 0x00 && target.nti.nai.abtAtqa[1] == 0x02) { 188 | printf("Read MIFARE Classic 4k (SAK: 18, ATQA: 00 02)\n"); 189 | } 190 | else { 191 | printf("Read unknown tag.\n"); 192 | } 193 | 194 | return mf_disconnect(0); 195 | } 196 | 197 | 198 | int mf_write_tag(const mf_tag_t* tag, mf_key_type_t key_type) { 199 | if (mf_connect()) 200 | return -1; // No need to disconnect here 201 | 202 | if (key_type == MF_KEY_UNLOCKED) { 203 | if (!mf_unlock()) { 204 | printf("Unlocked write requested, but unlock failed!\n"); 205 | return false; 206 | } 207 | } 208 | 209 | if (!mf_write_tag_internal(tag, ¤t_auth, key_type)) { 210 | printf("Write failed!\n"); 211 | return mf_disconnect(-1); 212 | } 213 | 214 | return mf_disconnect(0); 215 | } 216 | 217 | int mf_dictionary_attack(mf_tag_t* tag) { 218 | 219 | if (mf_connect()) { 220 | return -1; // No need to disconnect here 221 | } 222 | 223 | if (!mf_dictionary_attack_internal(tag)) { 224 | printf("Dictionary attack failed!\n"); 225 | return mf_disconnect(-1); 226 | } 227 | 228 | return mf_disconnect(0); 229 | } 230 | 231 | 232 | int mf_test_auth(const mf_tag_t* keys, 233 | mf_size_t size, 234 | mf_key_type_t key_type) { 235 | 236 | if (mf_connect()) { 237 | return -1; // No need to disconnect here 238 | } 239 | 240 | if (!mf_test_auth_internal(keys, size, key_type)) { 241 | printf("Test authentication failed!\n"); 242 | return mf_disconnect(-1); 243 | } 244 | 245 | return mf_disconnect(0); 246 | } 247 | 248 | 249 | bool mf_configure_device() { 250 | 251 | // Disallow invalid frame 252 | if (nfc_device_set_property_bool(device, NP_ACCEPT_INVALID_FRAMES, false) < 0) 253 | return false; 254 | 255 | // Disallow multiple frames 256 | if (nfc_device_set_property_bool(device, NP_ACCEPT_MULTIPLE_FRAMES, false) < 0) 257 | return false; 258 | 259 | // Make sure we reset the CRC and parity to chip handling. 260 | if (nfc_device_set_property_bool(device, NP_HANDLE_CRC, true) < 0) 261 | return false; 262 | 263 | if (nfc_device_set_property_bool(device, NP_HANDLE_PARITY, true) < 0) 264 | return false; 265 | 266 | // Disable ISO14443-4 switching in order to read devices that emulate 267 | // Mifare Classic with ISO14443-4 compliance. 268 | if (nfc_device_set_property_bool(device, NP_AUTO_ISO14443_4, false) < 0) 269 | return false; 270 | 271 | // Activate "easy framing" feature by default 272 | if (nfc_device_set_property_bool(device, NP_EASY_FRAMING, true) < 0) 273 | return false; 274 | 275 | // Deactivate the CRYPTO1 cipher, it may could cause problems when 276 | // still active 277 | if (nfc_device_set_property_bool(device, NP_ACTIVATE_CRYPTO1, false) < 0) 278 | return false; 279 | 280 | // Drop explicitely the field 281 | if (nfc_device_set_property_bool(device, NP_ACTIVATE_FIELD, false) < 0) 282 | return false; 283 | 284 | // Override default initialization option, only try to select a tag once. 285 | if (nfc_device_set_property_bool(device, NP_INFINITE_SELECT, false) < 0) 286 | return false; 287 | 288 | return true; 289 | } 290 | 291 | bool mf_select_target() { 292 | if (nfc_initiator_select_passive_target(device, 293 | mf_nfc_modulation, 294 | NULL, // init data 295 | 0, // init data len 296 | &target) < 0) { 297 | return false; 298 | } 299 | return true; 300 | } 301 | 302 | /** 303 | * Unlocking the card allows writing to block 0 of some pirate cards. 304 | */ 305 | bool mf_unlock() { 306 | static uint8_t abtHalt[4] = { 0x50, 0x00, 0x00, 0x00 }; 307 | 308 | // Special unlock command 309 | static const uint8_t abtUnlock1[1] = { 0x40 }; 310 | static const uint8_t abtUnlock2[1] = { 0x43 }; 311 | 312 | // Disable CRC and parity checking 313 | if (nfc_device_set_property_bool(device, NP_HANDLE_CRC, false) < 0) 314 | return false; 315 | 316 | // Disable easy framing. Use raw send/receive methods 317 | if (nfc_device_set_property_bool (device, NP_EASY_FRAMING, false) < 0) 318 | return false; 319 | 320 | // Initialize transmision 321 | iso14443a_crc_append(abtHalt, 2); 322 | transmit_bytes(abtHalt, 4); 323 | 324 | // Send unlock 325 | if (!transmit_bits (abtUnlock1, 7)) 326 | return false; 327 | 328 | if (!transmit_bytes (abtUnlock2, 1)) 329 | return false; 330 | 331 | // Reset reader configuration. CRC and easy framing. 332 | if (nfc_device_set_property_bool (device, NP_HANDLE_CRC, true) < 0) 333 | return false; 334 | if (nfc_device_set_property_bool (device, NP_EASY_FRAMING, true) < 0) 335 | return false; 336 | 337 | return true; 338 | } 339 | 340 | bool mf_read_tag_internal(mf_tag_t* tag, 341 | const mf_tag_t* keys, mf_key_type_t key_type) { 342 | mifare_param mp; 343 | 344 | static mf_tag_t buffer_tag; 345 | clear_tag(&buffer_tag); 346 | 347 | int error = 0; 348 | 349 | printf("Reading: ["); fflush(stdout); 350 | 351 | // Read the card from end to begin 352 | for (int block_it = (int)block_count(size) - 1; block_it >= 0; --block_it) { 353 | size_t block = (size_t)block_it; 354 | 355 | // Print progress for the unlocked read 356 | if (key_type == MF_KEY_UNLOCKED && is_trailer_block(block)) { 357 | printf("."); fflush(stdout); 358 | } 359 | 360 | // Authenticate everytime we reach a trailer block 361 | // unless we are doing an unlocked read 362 | if (key_type != MF_KEY_UNLOCKED && is_trailer_block(block)) { 363 | 364 | // Try to authenticate for the current sector 365 | uint8_t* key = key_from_tag(keys, key_type, block); 366 | if (!mf_authenticate(block, key, key_type)) { 367 | // Progress indication and error report 368 | printf("0x%02zx", block_to_sector(block)); 369 | if (block != 3) printf("."); 370 | fflush(stdout); 371 | 372 | block_it -= (int)sector_size(block) - 1; // Skip the rest of the sector blocks 373 | error = 1; 374 | } 375 | else { 376 | // Try to read the trailer (only to *read* the access bits) 377 | if (nfc_initiator_mifare_cmd(device, MC_READ, (uint8_t)block, &mp)) { 378 | // Copy the keys over to our tag buffer 379 | key_to_tag(&buffer_tag, keys->amb[block].mbt.abtKeyA, MF_KEY_A, block); 380 | key_to_tag(&buffer_tag, keys->amb[block].mbt.abtKeyB, MF_KEY_B, block); 381 | 382 | // Store the retrieved access bits in the tag buffer 383 | memcpy(buffer_tag.amb[block].mbt.abtAccessBits, 384 | mp.mpd.abtData + 6, 4); 385 | } else { 386 | printf ("\nUnable to read trailer block: 0x%02zx.\n", block); 387 | return false; 388 | } 389 | printf("."); fflush(stdout); // Progress indicator 390 | } 391 | } 392 | 393 | else { // I.e. not a sector trailer 394 | // Try to read out the block 395 | if (!nfc_initiator_mifare_cmd(device, MC_READ, (uint8_t)block, &mp)) { 396 | printf("\nUnable to read block: 0x%02zx.\n", block); 397 | return false; 398 | } 399 | memcpy(buffer_tag.amb[block].mbd.abtData, mp.mpd.abtData, 0x10); 400 | } 401 | } 402 | 403 | // Terminate progress indicator 404 | if (error) 405 | printf("] Auth errors in indicated sectors.\n"); 406 | else 407 | printf("] Success!\n"); 408 | 409 | // Success! Copy the data 410 | // todo: Or return static ptr? 411 | memcpy(tag, &buffer_tag, MF_4K); 412 | 413 | return true; 414 | } 415 | 416 | 417 | bool mf_write_tag_internal(const mf_tag_t* tag, 418 | const mf_tag_t* keys, 419 | mf_key_type_t key_type) { 420 | 421 | mifare_param mp; 422 | int error = 0; 423 | 424 | printf("Writing %s tag [", sprint_size(size)); fflush(stdout); 425 | 426 | // Process each sector in turn 427 | for (int header_block_it = sector_header_iterator(0); 428 | header_block_it != -1; 429 | header_block_it = sector_header_iterator(size)) { 430 | size_t header_block = (size_t)header_block_it; 431 | 432 | // Authenticate 433 | uint8_t* key = key_from_tag(keys, key_type, header_block); 434 | if (key_type != MF_KEY_UNLOCKED) { 435 | if (!mf_authenticate(header_block, key, key_type)) { 436 | // Progress indication and error report 437 | if (header_block != 0) printf("."); 438 | printf("0x%02zx", block_to_sector(header_block)); 439 | fflush(stdout); 440 | 441 | error = 1; 442 | continue; // Skip the rest of the sector blocks 443 | } 444 | } 445 | 446 | // Write the sectors blocks 447 | for (size_t block = header_block, trailer = block_to_trailer(header_block); 448 | block < trailer; ++block) { 449 | 450 | // First block on tag is read only - skip it unless unlocked 451 | if (block == 0 && key_type != MF_KEY_UNLOCKED) 452 | continue; 453 | 454 | // Try to write the data block 455 | memcpy (mp.mpd.abtData, tag->amb[block].mbd.abtData, 0x10); 456 | 457 | // do not write a block 0 with incorrect BCC - card will be made invalid! 458 | if (block == 0) { 459 | if((mp.mpd.abtData[0] ^ mp.mpd.abtData[1] ^ mp.mpd.abtData[2] ^ 460 | mp.mpd.abtData[3] ^ mp.mpd.abtData[4]) != 0x00) { 461 | printf ("\nError: incorrect BCC in MFD file!\n"); // ADD DATA 462 | return false; 463 | } 464 | } 465 | 466 | // Write the data block 467 | if (!nfc_initiator_mifare_cmd(device, MC_WRITE, (uint8_t)block, &mp)) { 468 | printf("\nUnable to write block: 0x%02zx.\n", block); 469 | return false; 470 | } 471 | } 472 | 473 | // Auth ok and sector read ok, finish up by reading trailer 474 | size_t trailer_block = block_to_trailer(header_block); 475 | memcpy (mp.mpd.abtData, tag->amb[trailer_block].mbt.abtKeyA, 6); 476 | memcpy (mp.mpd.abtData + 6, tag->amb[trailer_block].mbt.abtAccessBits, 4); 477 | memcpy (mp.mpd.abtData + 10, tag->amb[trailer_block].mbt.abtKeyB, 6); 478 | 479 | // Try to write the trailer 480 | if (!nfc_initiator_mifare_cmd(device, MC_WRITE, (uint8_t)trailer_block, &mp)) { 481 | printf("\nUnable to write block: 0x%02zx.\n", trailer_block); 482 | return false; 483 | } 484 | 485 | printf("."); fflush(stdout); // Progress indicator 486 | } 487 | 488 | // Terminate progress indicator 489 | if (error) 490 | printf("] Auth errors in indicated sectors.\n"); 491 | else 492 | printf("] Success!\n"); 493 | 494 | return true; 495 | } 496 | 497 | 498 | bool mf_dictionary_attack_internal(mf_tag_t* tag) { 499 | 500 | // Tag buffer to swap in if we find all keys 501 | int all_keys_found = 1; 502 | static mf_tag_t buffer_tag; 503 | clear_tag(&buffer_tag); 504 | 505 | // Iterate over the start blocks in all sectors 506 | for (int block_it = sector_header_iterator(0); 507 | block_it != -1; 508 | block_it = sector_header_iterator(size)) { 509 | size_t block = (size_t)block_it; 510 | 511 | printf("Working on sector: %02zx [", block_to_sector(block)); 512 | 513 | const uint8_t* key_a = NULL; 514 | const uint8_t* key_b = NULL; 515 | 516 | // Iterate we run out of dictionary keys or the sector is cracked 517 | const key_list_t* key_it = dictionary_get(); 518 | while(key_it && (key_a == NULL || key_b == NULL)) { 519 | 520 | // Try to authenticate for the current sector 521 | if (key_a == NULL && 522 | mf_authenticate(block, key_it->key, MF_KEY_A)) { 523 | key_a = key_it->key; 524 | } 525 | 526 | // Try to authenticate for the current sector 527 | if (key_b == NULL && 528 | mf_authenticate(block, key_it->key, MF_KEY_B)) { 529 | key_b = key_it->key; 530 | } 531 | 532 | key_it = key_it->next; 533 | 534 | printf("."); fflush(stdout); // Progress indicator 535 | } 536 | 537 | printf("]\n"); 538 | 539 | printf(" A Key: "); 540 | if (key_a) { 541 | printf("%s\n", sprint_key(key_a)); 542 | 543 | // Optimize dictionary by moving key to the front 544 | dictionary_add(key_a); 545 | 546 | // Save key in the buffer 547 | key_to_tag(&buffer_tag, key_a, MF_KEY_A, block); 548 | } 549 | else { 550 | all_keys_found = 0; 551 | printf("Not found\n"); 552 | } 553 | 554 | printf(" B Key: "); 555 | if (key_b) { 556 | printf("%s\n", sprint_key(key_b)); 557 | 558 | // Optimize dictionary by moving key to the front 559 | dictionary_add(key_b); 560 | 561 | // Save key in the buffer 562 | key_to_tag(&buffer_tag, key_b, MF_KEY_B, block); 563 | } 564 | else { 565 | all_keys_found = 0; 566 | printf("Not found\n"); 567 | } 568 | 569 | } 570 | 571 | if (all_keys_found) 572 | printf("All keys were found\n"); 573 | 574 | // Use the found keys 575 | memcpy(tag, &buffer_tag, MF_4K); 576 | 577 | return true; 578 | } 579 | 580 | 581 | bool mf_test_auth_internal(const mf_tag_t* keys, 582 | mf_size_t size, 583 | mf_key_type_t key_type) { 584 | 585 | printf("xS T Key Status\n"); 586 | printf("----------------------------\n"); 587 | 588 | for (int block_it = sector_header_iterator(0); 589 | block_it != -1; 590 | block_it = sector_header_iterator(size)) { 591 | size_t block = (size_t)block_it; 592 | 593 | uint8_t* key = key_from_tag(keys, key_type, block); 594 | printf("%02zx %c %s ", 595 | block_to_sector(block), 596 | key_type, 597 | sprint_key(key)); 598 | 599 | 600 | if (!mf_authenticate(block, key, key_type)) { 601 | printf("Failure"); 602 | } 603 | else { 604 | printf("Success"); 605 | } 606 | 607 | printf("\n"); 608 | } 609 | 610 | return true; 611 | } 612 | 613 | 614 | bool mf_authenticate(size_t block, const uint8_t* key, mf_key_type_t key_type) { 615 | 616 | mifare_param mp; 617 | 618 | // Set the authentication information (uid) 619 | memcpy(mp.mpa.abtAuthUid, target.nti.nai.abtUid + target.nti.nai.szUidLen - 4, 4); 620 | 621 | // Select key for authentication 622 | mifare_cmd mc = (key_type == MF_KEY_A) ? MC_AUTH_A : MC_AUTH_B; 623 | 624 | // Set the key 625 | memcpy(mp.mpa.abtKey, key, 6); 626 | 627 | // Try to authenticate for the current sector 628 | if (nfc_initiator_mifare_cmd(device, mc, (uint8_t)block, &mp)) 629 | return true; 630 | 631 | // Do the hand shaking again if auth failed 632 | nfc_initiator_select_passive_target(device, mf_nfc_modulation, 633 | NULL, 0, &target); 634 | 635 | return false; 636 | } 637 | 638 | bool transmit_bits(const uint8_t *pbtTx, const size_t szTxBits) 639 | { 640 | // Transmit the bit frame command, we don't use the arbitrary parity feature 641 | if ((szRxBits = nfc_initiator_transceive_bits(device, pbtTx, szTxBits, NULL, abtRx, sizeof(abtRx), NULL)) < 0) 642 | return false; 643 | 644 | return true; 645 | } 646 | 647 | 648 | bool transmit_bytes(const uint8_t *pbtTx, const size_t szTx) 649 | { 650 | // Transmit the command bytes 651 | if (nfc_initiator_transceive_bytes(device, pbtTx, szTx, abtRx, sizeof(abtRx), 0) < 0) 652 | return false; 653 | 654 | return true; 655 | } 656 | -------------------------------------------------------------------------------- /mifare_ctrl.h: -------------------------------------------------------------------------------- 1 | #ifndef MIFARE_CTRL__H 2 | #define MIFARE_CTRL__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | * 22 | * Parts of code used in this file are from the GNU readline library file 23 | * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc 24 | */ 25 | 26 | #include "tag.h" 27 | #include "dictionary.h" 28 | 29 | /** 30 | * Connect to an nfc device. Then read the tag data, authenticating with the 31 | * 'current_auth' keys of specified type, and store it in the 32 | * 'current_tag' state variable. Finally, disconnect from the device. 33 | * If there are authentication errors, those sectors will be set to 34 | * all zeroes. 35 | * Return 0 on success != 0 on failure. 36 | */ 37 | int mf_read_tag(mf_tag_t* tag, mf_key_type_t key_type); 38 | 39 | /** 40 | * Connect to an nfc device. The write the tag data, authenticating with 41 | * the 'current_auth' keys of specified type. Finally, disconnect from 42 | * the device. If there are authentication errors, those sectors will 43 | * not be written. 44 | * If the key type is set to MF_UNLOCKED, try to unlock the card prior to 45 | * write. This allows some pirate cards to write block 0. 46 | * Return 0 on success != 0 on failure. 47 | */ 48 | int mf_write_tag(const mf_tag_t* tag, mf_key_type_t key_type); 49 | 50 | /** 51 | * Connect to an nfc device. Then, for each sector in turn, try keys in the 52 | * dictionary for authentication. Report success or failure. If a key 53 | * is found, set it in the state variable 'current_auth'. Finally, 54 | * disconnect from the device. 55 | * Return 0 on success != 0 on failure. 56 | */ 57 | int mf_dictionary_attack(mf_tag_t* tag); 58 | 59 | /** 60 | * Connect to an nfc device. Then test the keys in the 'current_auth' 61 | * by trying to authenticate to the sectors of the tag. Report success 62 | * or failure for each sector. Finally, disconnect from the device. 63 | * Return 0 on success != 0 on failure. 64 | */ 65 | int mf_test_auth(const mf_tag_t* keys, 66 | mf_size_t size, 67 | mf_key_type_t key_type); 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /spec_parser.y: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | 21 | %{ 22 | #include 23 | #include 24 | #include 25 | #include "spec_syntax.h" 26 | #include "util.h" 27 | 28 | /* #define YYDEBUG 1 */ 29 | /* sp_debug = 1; */ 30 | 31 | #define YYERROR_VERBOSE 1 32 | 33 | struct YYLTYPE; 34 | 35 | int sp_lex(void); 36 | void sp_error(const char* s, ...); 37 | void sp_lerror(struct YYLTYPE loc, const char* s, ...); 38 | %} 39 | 40 | %union { 41 | type_t* type_t_ptr; 42 | field_t* field_t_ptr; 43 | field_list_t* field_list_t_ptr; 44 | char* string; 45 | int integer; 46 | } 47 | 48 | %token BYTE "Byte" 49 | %token BIT "Bit" 50 | %token IDENTIFIER "name" 51 | %token DEC_NUM "dec-number" 52 | %token HEX_NUM "hex-number" 53 | %token end_of_file 0 "end-of-file" 54 | 55 | %type data_type 56 | %type primitive_data_type 57 | %type named_composite_type_decl 58 | %type composite_type_decl 59 | %type field_decl 60 | %type field_decl_list 61 | %type number 62 | 63 | %destructor { free($$); } IDENTIFIER DEC_NUM HEX_NUM 64 | %destructor { free_field($$); } field_decl 65 | %destructor { free_field($$->field); 66 | free($$); 67 | } composite_type_decl field_decl_list 68 | %destructor { if ($$ && $$->composite_extras) 69 | free_composite_type($$); 70 | } named_composite_type_decl primitive_data_type data_type 71 | 72 | %% /* Grammar rules and actions follow. */ 73 | 74 | input 75 | : /* empty */ { } 76 | | input named_composite_type_decl { (void) $2; } 77 | | input '.' composite_type_decl { 78 | if (tt_get_type(".")) { 79 | // Error - the root type has allready been defined 80 | sp_lerror(@1, "Root type '.' allready defined."); 81 | YYERROR; // abort and initiate error recovery 82 | } 83 | 84 | // Create the type (named '.') 85 | type_t* t = make_composite_type(strdup(".")); 86 | t->composite_extras->fields = $3; 87 | t->composite_extras->decl_status = COMPLETE_DECL; 88 | tt_add_type(t); 89 | type_root = t; 90 | } 91 | ; 92 | 93 | named_composite_type_decl 94 | : IDENTIFIER composite_type_decl { 95 | 96 | type_t* t = tt_get_type($1); 97 | if (t && t->composite_extras->decl_status == COMPLETE_DECL) { 98 | // Error - the type has been defined once before 99 | sp_lerror(@1, "Re-definition of type '%s'", $1); 100 | $$ = NULL; 101 | YYERROR; // abort and initiate error recovery 102 | } 103 | 104 | // If it's the first time we see the type, create it 105 | if(t == NULL) { 106 | t = make_composite_type($1); 107 | tt_add_type(t); 108 | } 109 | 110 | t->composite_extras->fields = $2; 111 | t->composite_extras->decl_status = COMPLETE_DECL; 112 | $$ = t; 113 | } 114 | ; 115 | 116 | composite_type_decl 117 | : '{' field_decl_list '}' { 118 | $$ = $2; 119 | } 120 | ; 121 | 122 | field_decl_list 123 | : /* empty */ { 124 | $$ = NULL; 125 | } 126 | | field_decl_list field_decl { 127 | if ($1 == NULL) { 128 | $$ = append_field(NULL, $2); 129 | } 130 | else { 131 | if ($2 == NULL) { 132 | $$ = NULL; 133 | } 134 | else if ($2->name == NULL || get_field($1, $2->name) == NULL) { 135 | // If it doesn't exist, then all is well. Add it. 136 | $$ = append_field($1, $2); 137 | } 138 | else { 139 | // If it allready exists, we have a semantic error. 140 | sp_lerror(@2, "A field with the name '%s' is allready defined.", 141 | $2->name); 142 | $$ = $1; 143 | YYERROR; // abort and initiate error recovery 144 | } 145 | } 146 | } 147 | | field_decl_list error { 148 | $$ = $1; 149 | yyclearin; 150 | YYERROR; 151 | } 152 | ; 153 | 154 | field_decl 155 | : data_type IDENTIFIER { 156 | $$ = make_field($2, $1, 1); 157 | } 158 | | data_type '-' { 159 | $$ = make_field(NULL, $1, 1); // Anonymous 160 | } 161 | | data_type '[' number ']' IDENTIFIER { 162 | $$ = make_field($5, $1, $3); 163 | } 164 | | data_type '[' number ']' '-' { 165 | $$ = make_field(NULL, $1, $3); 166 | } 167 | 168 | // Error handling 169 | | data_type error { 170 | (void) $1; $$ = NULL; YYERROR; 171 | } 172 | | data_type '[' number ']' error { 173 | (void) $1; $$ = NULL; YYERROR; 174 | } 175 | | data_type '[' error ']' IDENTIFIER { 176 | (void) $1; (void) $5; $$ = NULL; YYERROR; 177 | } 178 | | data_type '[' error ']' '-' { 179 | (void) $1; $$ = NULL; YYERROR; 180 | } 181 | | data_type '[' error ']' error { 182 | (void) $1; $$ = NULL; YYERROR; 183 | } 184 | ; 185 | 186 | data_type 187 | : composite_type_decl { 188 | type_t* t; 189 | t = make_composite_type(NULL); // Anonymous 190 | tt_add_type(t); 191 | t->composite_extras->fields = $1; 192 | t->composite_extras->decl_status = COMPLETE_DECL; 193 | $$ = t; 194 | } 195 | | primitive_data_type { $$ = $1; } 196 | | IDENTIFIER { 197 | type_t* t = tt_get_type($1); 198 | if (t) { 199 | $$ = t; // Re-discovered a known type 200 | } 201 | else { 202 | // This is a new type name - decl will hopefully come later. 203 | t = make_composite_type($1); 204 | t->composite_extras->decl_status = PARTIAL_DECL; 205 | tt_add_type(t); 206 | $$ = t; 207 | } 208 | } 209 | ; 210 | 211 | primitive_data_type 212 | : BYTE { $$ = &byte_type; } 213 | | BIT { $$ = &bit_type; } 214 | ; 215 | 216 | number 217 | : DEC_NUM { $$ = strtol($1, NULL, 10); free($1); } 218 | | HEX_NUM { $$ = strtol($1, NULL, 16); free($1); } 219 | ; 220 | 221 | %% 222 | 223 | void sp_lerror(struct YYLTYPE t, const char* s, ...) { 224 | va_list ap; 225 | va_start(ap, s); 226 | 227 | if(t.first_line) { 228 | if (t.last_line == t.first_line) 229 | fprintf(stderr, "Error:%d:%d: ", t.first_line, t.first_column); 230 | else 231 | fprintf(stderr, "Error:%d-%d: ", t.first_line, t.last_line); 232 | } 233 | vfprintf(stderr, s, ap); 234 | fprintf(stderr, "\n"); 235 | } 236 | -------------------------------------------------------------------------------- /spec_syntax.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "util.h" 25 | #include "spec_syntax.h" 26 | 27 | // Forward declarations 28 | int sp_parse(); 29 | extern FILE* sp_in; 30 | 31 | // Parse the input file and set up type_root and instance_root. 32 | // Return 0 on success. 33 | int spec_import(FILE* input) { 34 | 35 | // Parse the input to build a type hierarchy 36 | sp_in = input; 37 | if (sp_parse()) { 38 | printf("Syntax Error: Specification not imported\n"); 39 | goto error; 40 | } 41 | 42 | // Check for missing definitions 43 | type_t* partial = tt_contains_partial_types(); 44 | if (partial) { 45 | printf("Error: Incomplete declaration '%s' in specification.\n", 46 | partial->composite_extras->name); 47 | goto error; 48 | } 49 | 50 | // Make sure we have a root type defined 51 | if (type_root == NULL) { 52 | printf("Error: No root type '.' found in specification.\n"); 53 | goto error; 54 | } 55 | 56 | // Create the instance tree 57 | instance_root = make_instance_tree(type_root); 58 | 59 | printf("Specification sucessfully imported.\n"); 60 | 61 | return 0; 62 | 63 | error: 64 | clear_instance_tree(); 65 | tt_clear(); 66 | return 1; 67 | } 68 | 69 | // The primitive Byte type 70 | type_t byte_type = { 71 | .type_category = BYTE_TYPE, 72 | .composite_extras = NULL, 73 | }; 74 | 75 | // The primitive Bit type 76 | type_t bit_type = { 77 | .type_category = BIT_TYPE, 78 | .composite_extras = NULL, 79 | }; 80 | 81 | // Allocate and return a composite type instance. The type will assume 82 | // ownership of the heap allocated name. 83 | type_t* make_composite_type(char* name) { 84 | type_t* t = malloc(sizeof(type_t)); 85 | t->type_category = COMPOSITE_TYPE; 86 | t->composite_extras = (composite_type_extras_t*) 87 | malloc(sizeof(composite_type_extras_t)); 88 | 89 | if (name) 90 | t->composite_extras->name = name; 91 | else 92 | t->composite_extras->name = NULL; // Anonymous type 93 | 94 | t->composite_extras->fields = NULL; 95 | return t; 96 | } 97 | 98 | // Free a composite type. This function will also free it's fields. 99 | void free_composite_type(type_t* t) { 100 | 101 | // Free the fields 102 | field_list_t* iter = t->composite_extras->fields; 103 | while (iter) { 104 | field_list_t* tmp = iter; 105 | iter = iter->next_; 106 | free_field(tmp->field); 107 | free(tmp); 108 | } 109 | 110 | // Free the type data 111 | free(t->composite_extras->name); 112 | free(t->composite_extras); 113 | free(t); 114 | } 115 | 116 | // Allocate a new field with the given parameters. Anonymous '-' 117 | // filler fields use NULL as name. The field will assume ownership of 118 | // the heap allocated name. 119 | field_t* make_field(char* name, type_t* type, size_t length) { 120 | field_t* f = (field_t*) malloc(sizeof(field_t)); 121 | f->name = name; // NULL for fillers 122 | f->type = type; 123 | f->length = length; 124 | return f; 125 | } 126 | 127 | // Free the memory used by a field. 128 | void free_field(field_t* field) { 129 | if (field == NULL) 130 | return; 131 | free(field->name); 132 | free(field); 133 | } 134 | 135 | // Add a field to an existing list of fields or, if the field_list 136 | // parameter is NULL, create a new field list. The order of fields is 137 | // significant and this function will append the field to the end of 138 | // the field_list. 139 | field_list_t* append_field(field_list_t* field_list, field_t* field) { 140 | 141 | // Create the list node 142 | field_list_t* flist = (field_list_t*) malloc(sizeof(field_list_t)); 143 | flist->field = field; 144 | flist->next_ = NULL; 145 | 146 | // Create the list or append to the end 147 | if (field_list != NULL) { 148 | field_list_t* it = field_list; 149 | while(it->next_) 150 | it = it->next_; 151 | it->next_ = flist; 152 | } 153 | else { 154 | field_list = flist; // Won't effect the inarg 155 | } 156 | 157 | // Return the start of the list 158 | return field_list; 159 | } 160 | 161 | // Search the field list for a field with the given name 162 | field_t* get_field(field_list_t* field_list, const char* name) { 163 | if (name == NULL || field_list == NULL) 164 | return NULL; 165 | 166 | field_list_t* it = field_list; 167 | while(it) { 168 | field_t* f = it->field; 169 | if (f && f->name && strcmp(f->name, name) == 0) 170 | return f; 171 | it = it->next_; 172 | } 173 | 174 | // Not found 175 | return NULL; 176 | } 177 | 178 | 179 | // The global instance of the type table. If there isn't any, the 180 | // variable will be NULL. All the type table operations (tt_) operate 181 | // on this global variable. 182 | type_table_t* type_table = NULL; 183 | 184 | // The root type of the type hierarchy 185 | type_t* type_root = NULL; 186 | 187 | 188 | // Internal functions for allocating and freeing type table list nodes. 189 | type_table_t* tt_make_node_(type_t* t); 190 | void tt_free_node_(type_table_t* tt); 191 | 192 | // Clear the type table - freeing the memory used by the table and by 193 | // all the types. 194 | void tt_clear() { 195 | 196 | // Reset the root 197 | type_root = NULL; 198 | 199 | // Free all types and the table itself 200 | type_table_t* it = type_table; 201 | while(it) { 202 | type_table_t* next = it->next_; 203 | free_composite_type(it->type); 204 | tt_free_node_(it); 205 | it = next; 206 | }; 207 | 208 | type_table = NULL; 209 | } 210 | 211 | // Add a type to the type table. 212 | type_t* tt_add_type(type_t* t) { 213 | if (type_table == NULL) { 214 | type_table = tt_make_node_(t); 215 | } 216 | else { 217 | type_table_t* it = type_table; 218 | while(it->next_) 219 | it = it->next_; 220 | it->next_ = tt_make_node_(t); 221 | } 222 | return t; 223 | } 224 | 225 | // Search the type table for a type with the given name. The first 226 | // type found will be returned. If no type is found, NULL is returned. 227 | type_t* tt_get_type(const char* type_name) { 228 | if (type_name == NULL) 229 | return NULL; 230 | 231 | type_table_t* it = type_table; 232 | while(it) { 233 | // Anonymous types (name == NULL) will never match 234 | if (it->type->composite_extras->name && 235 | strcmp(type_name, it->type->composite_extras->name) == 0) 236 | return it->type; // Type was found! 237 | it = it->next_; 238 | } 239 | 240 | // Not found 241 | return NULL; 242 | } 243 | 244 | // Check if there are any partially declared types in the type 245 | // table. Return a pointer to the first incomplete type or NULL if 246 | // none exists. 247 | type_t* tt_contains_partial_types() { 248 | type_table_t* it = type_table; 249 | while(it) { 250 | if (it->type->composite_extras->decl_status == PARTIAL_DECL) 251 | return it->type; 252 | it = it->next_; 253 | } 254 | return NULL; 255 | } 256 | 257 | // Allocate a new list entry for the type table 258 | type_table_t* tt_make_node_(type_t* t) { 259 | type_table_t* tt = malloc(sizeof(type_table_t)); 260 | tt->type = t; 261 | tt->next_ = NULL; 262 | return tt; 263 | } 264 | 265 | // Free a type table list entry (this won't free the type) 266 | void tt_free_node_(type_table_t* tt) { 267 | free(tt); 268 | } 269 | 270 | 271 | // The global variable representing the root instance; it is an 272 | // instanciation of the '.' type. 273 | instance_t* instance_root = NULL; 274 | 275 | // Forward decls of internal functions used during creation and 276 | // destruction of the instance tree. 277 | void clear_instance_tree_(instance_t* root); 278 | void make_instance_(instance_t* root, 279 | size_t* obytes, size_t* obits, 280 | size_t* sbytes, size_t* sbits); 281 | instance_t* make_byte_instance_(field_t* field, size_t* obytes, size_t* obits); 282 | instance_t* make_bit_instance_(field_t* field, size_t* obytes, size_t* obits); 283 | instance_t* make_composite_instance_(field_t* field, 284 | size_t* obytes, size_t* obits); 285 | instance_list_t* append_instance_(instance_list_t** end_ptr, 286 | instance_t* new_field); 287 | 288 | 289 | // Create an instance tree matching the type tree starting at 290 | // type_root. The global instance tree is constructed with type_root '.'. 291 | instance_t* make_instance_tree(type_t* type_root) { 292 | instance_t* root = malloc(sizeof(instance_t)); 293 | root->offset_bytes = 0; 294 | root->offset_bits = 0; 295 | root->size_bytes = 0; 296 | root->size_bits = 0; 297 | root->field = make_field(strdup("."), type_root, 1); 298 | root->fields = NULL; 299 | 300 | size_t obytes = 0; 301 | size_t obits = 0; 302 | make_instance_(root, &obytes, &obits, 303 | &(root->size_bytes), &(root->size_bits)); 304 | return root; 305 | } 306 | 307 | // Clear the global instance tree. Free it and set instance_root NULL 308 | void clear_instance_tree() { 309 | if (instance_root == NULL) 310 | return; 311 | clear_instance_tree_(instance_root); 312 | instance_root = NULL; 313 | } 314 | 315 | /* Get the child instance with a given name. Only look to children, */ 316 | /* not grand children. If no child with the given name exists, return */ 317 | /* null. */ 318 | instance_t* get_instance_child(instance_t* inst, const char* name) { 319 | if (name == NULL) 320 | return NULL; 321 | 322 | return get_instance_child_n(inst, name, strlen(name)); 323 | } 324 | 325 | /** 326 | * Like get_instance_child(inst, name), but name does not have to be 327 | * null terminated. Instead the length of the name string is given by 328 | * the last argument. 329 | */ 330 | instance_t* get_instance_child_n(instance_t* inst, 331 | const char* name, 332 | size_t nlen) { 333 | 334 | if (inst == NULL || name == NULL || nlen == 0) 335 | return NULL; 336 | 337 | instance_list_t* iter = inst->fields; 338 | while(iter) { 339 | field_t* f = iter->instance->field; 340 | 341 | // Make sure the field has a name (isn't anaonymous) before comparing. 342 | if (f && f->name && 343 | strlen(f->name) == nlen && 344 | strncmp(f->name, name, nlen) == 0) 345 | return iter->instance; 346 | iter = iter->next_; 347 | } 348 | 349 | return NULL; 350 | } 351 | 352 | /** 353 | * Parse a specification path of the form '.fu.bar.baz' and return the 354 | * instance pointed to by baz. In case the path doesn't point to a 355 | * loaded instance, return NULL. */ 356 | instance_t* parse_spec_path(const char* path) { 357 | 358 | const char* lastpath; 359 | instance_t* inst; 360 | 361 | // Parse the path up to the last instance 362 | if (parse_partial_spec_path(path, &lastpath, &inst) != 0) 363 | return NULL; 364 | 365 | // Find and return the leaf node 366 | return get_instance_child(inst, lastpath); 367 | } 368 | 369 | /** 370 | * Parse the path to produce a parent section and an instance that 371 | * points to the head of the parent. 372 | * 373 | * The format is .fu.bar.ba(z). Where .fu.bar.ba is the path, fu, bar 374 | * and baz are nested fields. The function should return parent_end 375 | * pointing into path to the point after the last '.', i.e. to the 'b' 376 | * in the last 'ba'. parent_inst will point to bar. 377 | * 378 | * The function returns 0 on success. 379 | */ 380 | int parse_partial_spec_path(const char* path, 381 | const char** parent_end, 382 | instance_t** parent_inst) { 383 | 384 | // Check input. Paths start with '.' 385 | if (path == NULL || path[0] != '.') 386 | return 1; 387 | 388 | instance_t* inst = instance_root; 389 | const char* remaining_path = path + 1; 390 | 391 | char* tok_end; 392 | while((tok_end = strchr(remaining_path, '.')) != NULL) { 393 | // There is still a part of the path before the last '.' 394 | 395 | ptrdiff_t tok_name = tok_end - remaining_path; 396 | if (tok_name <= 0) 397 | return 1; 398 | 399 | inst = get_instance_child_n(inst, remaining_path, (size_t)tok_name); 400 | 401 | // Exit early (error in ancestor path) 402 | if (inst == NULL) 403 | return 1; 404 | 405 | // Remove the token and the '.' from the start of remaining string 406 | remaining_path = tok_end + 1; 407 | } 408 | 409 | // Set the output arguments 410 | if (parent_end != NULL) 411 | *parent_end = remaining_path; 412 | if (parent_inst != NULL) 413 | *parent_inst = inst; 414 | 415 | return 0; 416 | } 417 | 418 | // Count the number of fields in the instance 419 | int instance_fields_count(instance_t* inst) { 420 | if (inst == NULL) 421 | return 0; 422 | 423 | int count = 0; 424 | instance_list_t* it = inst->fields; 425 | while (it) { 426 | ++count; 427 | it = it->next_; 428 | } 429 | 430 | return count; 431 | } 432 | 433 | void clear_instance_tree_(instance_t* root) { 434 | instance_list_t* iter = root->fields; 435 | while (iter) { 436 | instance_list_t* tmp = iter->next_; 437 | clear_instance_tree_(iter->instance); 438 | free(iter); 439 | iter = tmp; 440 | } 441 | free(root); 442 | } 443 | 444 | void make_instance_(instance_t* root, 445 | size_t* obytes, size_t* obits, 446 | size_t* sbytes, size_t* sbits) { 447 | 448 | // Pointers to the matching type field and instance field beign processed. 449 | field_list_t* tf_iter = root->field->type->composite_extras->fields; 450 | instance_list_t* if_iter = root->fields; 451 | 452 | // Iterate over all the type fields and add matching instance fields. 453 | while(tf_iter) { 454 | field_t* f = tf_iter->field; 455 | 456 | // Create the child sub-tree or instance. 457 | instance_t* child; 458 | // Byte 459 | if (f->type == &byte_type) { 460 | child = make_byte_instance_(f, obytes, obits); 461 | } 462 | // Bit 463 | else if (f->type == &bit_type) { 464 | child = make_bit_instance_(f, obytes, obits); 465 | } 466 | // Composite type 467 | else { 468 | child = make_composite_instance_(f, obytes, obits); 469 | child->size_bytes = 0; 470 | child->size_bits = 0; 471 | 472 | make_instance_(child, obytes, obits, 473 | &(child->size_bytes), &(child->size_bits)); 474 | 475 | // Update size with bits % 8 476 | child->size_bytes = 477 | child->size_bytes * f->length + 478 | (child->size_bits * f->length) / 8; 479 | child->size_bits = (child->size_bits * f->length) % 8; 480 | } 481 | 482 | // Add the new instance or instance tree, to the root instance 483 | // field list. 484 | if_iter = if_iter == NULL ? 485 | append_instance_(&(root->fields), child) : 486 | append_instance_(&(if_iter->next_), child); 487 | 488 | // Note that we are changing the value of the input parameters here 489 | // this is part of the 'return value' of the function. Wrap the 490 | // bit size mod 8. 491 | *sbytes = *sbytes + child->size_bytes + (*sbits + child->size_bits) / 8; 492 | *sbits = (*sbits + child->size_bits) % 8; 493 | 494 | tf_iter = tf_iter->next_; 495 | } 496 | } 497 | 498 | instance_t* make_byte_instance_(field_t* field, size_t* obytes, size_t* obits) { 499 | instance_t* i = malloc(sizeof(instance_t)); 500 | i->field = field; 501 | i->fields = NULL; 502 | i->offset_bytes = *obytes; 503 | i->offset_bits = *obits; 504 | i->size_bytes = field->length; 505 | i->size_bits = 0; 506 | *obytes += i->size_bytes; 507 | // *obits += 0; 508 | return i; 509 | } 510 | 511 | instance_t* make_bit_instance_(field_t* field, size_t* obytes, size_t* obits) { 512 | instance_t* i = malloc(sizeof(instance_t)); 513 | i->field = field; 514 | i->fields = NULL; 515 | i->offset_bytes = *obytes; 516 | i->offset_bits = *obits; 517 | 518 | // Bits % 8, overflow in bytes field 519 | i->size_bytes = field->length / 8; 520 | i->size_bits = field->length % 8; 521 | *obytes += i->size_bytes + (*obits + i->size_bits) / 8; 522 | *obits = (*obits + i->size_bits) % 8; 523 | return i; 524 | } 525 | 526 | instance_t* make_composite_instance_(field_t* field, 527 | size_t* obytes, size_t* obits) { 528 | instance_t* i = malloc(sizeof(instance_t)); 529 | i->field = field; 530 | i->fields = NULL; 531 | i->offset_bytes = *obytes; 532 | i->offset_bits = *obits; 533 | 534 | // The size will be updated later, after recusion into all child fields. 535 | i->size_bytes = 0; 536 | i->size_bits = 0; 537 | return i; 538 | } 539 | 540 | instance_list_t* append_instance_(instance_list_t** end_ptr, 541 | instance_t* new_field) { 542 | instance_list_t* il = (instance_list_t*) malloc(sizeof(instance_list_t)); 543 | il->instance = new_field; 544 | il->next_ = NULL; 545 | *end_ptr = il; 546 | return il; 547 | } 548 | 549 | // Forward declaration 550 | void print_instance_tree_(instance_t* i, int indent); 551 | void print_instance_(instance_t* i); 552 | 553 | 554 | void print_instance_tree() { 555 | if (instance_root == NULL) { 556 | printf("No specification loaded.\n"); 557 | return; 558 | } 559 | 560 | printf("[%zu, %zu] [%zu, %zu] -- . (root)\n", 561 | instance_root->offset_bytes, 562 | instance_root->offset_bits, 563 | instance_root->size_bytes, 564 | instance_root->size_bits); 565 | 566 | print_instance_tree_(instance_root, 1); 567 | } 568 | 569 | void print_instance_tree_(instance_t* root, int indent) { 570 | 571 | // For each field of the root instance 572 | instance_list_t* il = root->fields; 573 | while(il) { 574 | 575 | // Indent 576 | int count = (indent - 1) * 2; 577 | while(count--) 578 | printf(" "); 579 | printf("+- "); 580 | 581 | // Print instance field 582 | instance_t* inst = il->instance; 583 | print_instance_(inst); 584 | if (inst->field->type->composite_extras != NULL) 585 | print_instance_tree_(inst, indent + 1); 586 | 587 | il = il->next_; 588 | } 589 | } 590 | 591 | void print_instance_(instance_t* i) { 592 | if (i->field->type == &byte_type) { 593 | printf("[%zu, %zu] [%zu, %zu] -- Byte[%zu] %s\n", 594 | i->offset_bytes, 595 | i->offset_bits, 596 | i->size_bytes, 597 | i->size_bits, 598 | i->field->length, 599 | i->field->name); 600 | } 601 | else if (i->field->type == &bit_type) { 602 | printf("[%zu, %zu] [%zu, %zu] -- Bit[%zu] %s\n", 603 | i->offset_bytes, 604 | i->offset_bits, 605 | i->size_bytes, 606 | i->size_bits, 607 | i->field->length, 608 | i->field->name); 609 | } 610 | else { 611 | printf("[%zu, %zu] [%zu, %zu] -- %s[%zu] %s\n", 612 | i->offset_bytes, 613 | i->offset_bits, 614 | i->size_bytes, 615 | i->size_bits, 616 | i->field->type->composite_extras->name, 617 | i->field->length, 618 | i->field->name); 619 | } 620 | } 621 | -------------------------------------------------------------------------------- /spec_syntax.h: -------------------------------------------------------------------------------- 1 | #ifndef SPEC_SYNTAX__H 2 | #define SPEC_SYNTAX__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | */ 22 | 23 | #include 24 | 25 | typedef enum { 26 | COMPOSITE_TYPE, 27 | BYTE_TYPE, 28 | BIT_TYPE, 29 | } type_category_t; 30 | 31 | typedef enum { 32 | PARTIAL_DECL, 33 | COMPLETE_DECL, 34 | } type_decl_status_t; 35 | 36 | typedef struct type_t type_t; 37 | typedef struct field_t field_t; 38 | typedef struct field_list_t field_list_t; 39 | typedef struct composite_type_extras_t composite_type_extras_t; 40 | typedef struct type_table_t type_table_t; 41 | typedef struct instance_t instance_t; 42 | typedef struct instance_list_t instance_list_t; 43 | 44 | // Parse the input file and set up type_root and instance_root. 45 | // Return 0 on success. 46 | int spec_import(FILE* input); 47 | 48 | /** 49 | * A struct representing a data type in the specification 50 | * language. There are two primitive types, Bit and Bytte. All other 51 | * types are user defined and use the composite_extras field to 52 | * express the type details. 53 | * 54 | * The primitive types are allocated statically (constants), while all 55 | * the composite types are allocated dynamically. 56 | */ 57 | struct type_t { 58 | type_category_t type_category; 59 | composite_type_extras_t* composite_extras; 60 | }; 61 | 62 | // The primitive type instances 63 | extern type_t byte_type; 64 | extern type_t bit_type; 65 | 66 | /** 67 | * A composite type is made up of an ordered list of fields. A field 68 | * is a named use of another type as an array. A field array of length 69 | * 1 can be treated without the array syntax in the language; but is 70 | * represented like all other fields. 71 | */ 72 | struct field_t { 73 | char* name; // Field name 74 | type_t* type; 75 | size_t length; 76 | }; 77 | 78 | /** 79 | * Type representing the ordered list of fields in a composite type. 80 | */ 81 | struct field_list_t { 82 | field_t* field; 83 | field_list_t* next_; 84 | }; 85 | 86 | /** 87 | * This structure represents the content of a user defined type. It 88 | * holds the name and the fields of the type. 89 | * 90 | * The type also has a flag to indicate if it has been fully 91 | * declared. The specification language allows the use of types before 92 | * they have been declared. Once the complete specification has been 93 | * parsed, all types must be declared. 94 | */ 95 | struct composite_type_extras_t { 96 | char* name; // Type name 97 | type_decl_status_t decl_status; 98 | field_list_t* fields; // All fields of the type or NULL. 99 | }; 100 | 101 | // Allocate and return a composite type instance. The type will assume 102 | // ownership of the heap allocated name. 103 | type_t* make_composite_type(char* name); 104 | 105 | // Free a composite type. This function will also free it's fields. 106 | void free_composite_type(type_t* t); 107 | 108 | // Allocate a new field with the given parameters. Anonymous '-' 109 | // filler fields use NULL as name. The field will assume ownership of 110 | // the heap allocated name. 111 | field_t* make_field(char* name, type_t* type, size_t length); 112 | 113 | // Free the memory used by a field. 114 | void free_field(field_t* field); 115 | 116 | // Add a field to an existing list of fields. The order of fields is 117 | // significant and this function will append the field to the end of 118 | // the field_list. 119 | field_list_t* append_field(field_list_t* field_list, field_t* field); 120 | 121 | // Search the field list for a field with the given name 122 | field_t* get_field(field_list_t* field_list, const char* name); 123 | 124 | 125 | /** 126 | * A 'table' of all the types in the language. This is part of the 127 | * output from the parsing process. The table is actually a list and 128 | * operations are typically O(n^2); but since there will probably 129 | * never be more than 50 types, this should be ok. 130 | */ 131 | struct type_table_t { 132 | type_t* type; 133 | type_table_t* next_; 134 | }; 135 | 136 | // The global instance of the type table. If there isn't any, the 137 | // variable will be NULL. All the type table operations (tt_) operate 138 | // on this global variable. 139 | extern type_table_t* type_table; 140 | 141 | // The root type of the type hierarchy 142 | extern type_t* type_root; 143 | 144 | // Clear the type table - freeing the memory used by the table and by 145 | // all the types. 146 | void tt_clear(); 147 | 148 | // Add a type to the type table. 149 | type_t* tt_add_type(type_t* t); 150 | 151 | // Search the type table for a type with the given name. The first 152 | // type found will be returned. If no type is found, NULL is returned. 153 | type_t* tt_get_type(const char* type_name); 154 | 155 | // Check if there are any partially declared types in the type 156 | // table. Return a pointer to the first incomplete type or NULL if 157 | // none exists. 158 | type_t* tt_contains_partial_types(); 159 | 160 | /** 161 | * Type representing instances of types in the spec language. The same 162 | * type can be instantiated several times in different spec types and 163 | * fields. The instances map agains type fields and thus contains a 164 | * length field. 165 | * 166 | * The size field is inclusive of the instance and all it's child 167 | * instances. The bit size field will be < 8; larger bit fields in the 168 | * type spec will be included in the byte field. 169 | */ 170 | struct instance_t { 171 | size_t offset_bytes; 172 | size_t offset_bits; 173 | size_t size_bytes; 174 | size_t size_bits; 175 | 176 | field_t* field; 177 | 178 | instance_list_t* fields; 179 | }; 180 | 181 | /** 182 | * Type representing the ordered list of instance fields in a 183 | * composite type instance. 184 | */ 185 | struct instance_list_t { 186 | instance_t* instance; 187 | instance_list_t* next_; 188 | }; 189 | 190 | // The global variable representing the root instance; it is an 191 | // instanciation of the '.' type. 192 | extern instance_t* instance_root; 193 | 194 | // Create an instance tree matching the type tree starting at 195 | // root_type. The global instance tree is constructed with root_type '.'. 196 | instance_t* make_instance_tree(type_t* root_type); 197 | 198 | // Clear the global instance tree. Free it and set instance_tree NULL 199 | void clear_instance_tree(); 200 | 201 | // Print a representation of the instance hierarchy 202 | void print_instance_tree(); 203 | 204 | /** 205 | * Get the child instance with a given name. Only look to children, 206 | * not grand children. If no child with the given name exists, return 207 | * null. 208 | */ 209 | instance_t* get_instance_child(instance_t* inst, const char* name); 210 | 211 | /** 212 | * Like get_instance_child(inst, name), but name does not have to be 213 | * null terminated. Instead the length of the name string is given by 214 | * the last argument. 215 | */ 216 | instance_t* get_instance_child_n(instance_t* inst, const char* name, size_t nlen); 217 | 218 | /** 219 | * Parse a specification path of the form '.fu.bar.baz' and return the 220 | * instance pointed to by baz. In case the path doesn't point to a 221 | * loaded instance, return NULL. */ 222 | instance_t* parse_spec_path(const char* path); 223 | 224 | /** 225 | * Parse the path to produce a parent section and an instance that 226 | * points to the head of the parent. 227 | * 228 | * The format is .fu.bar.ba(z). Where .fu.bar.ba is the path, fu, bar 229 | * and baz are nested fields. The function should return parent_end 230 | * pointing into path to the point after the last '.', i.e. to the 'b' 231 | * in the last 'ba'. parent_inst will point to bar. 232 | * 233 | * The function returns 0 on success. 234 | */ 235 | int parse_partial_spec_path(const char* path, 236 | const char** parent_end, 237 | instance_t** parent_inst); 238 | 239 | // Return the number of fields the instance has. 0 if inst is NULL. 240 | int instance_fields_count(instance_t* inst); 241 | 242 | #endif 243 | -------------------------------------------------------------------------------- /spec_tokenizer.l: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | %{ 21 | 22 | #include 23 | #include 24 | #include "util.h" 25 | #include "spec_syntax.h" 26 | 27 | #include "libsp_a-spec_parser.h" 28 | 29 | int colnum = 0; 30 | 31 | #define YY_USER_ACTION { \ 32 | sp_lloc.first_line = sp_lineno; \ 33 | sp_lloc.first_column = colnum; \ 34 | colnum = colnum + sp_leng; \ 35 | sp_lloc.last_column=colnum; \ 36 | sp_lloc.last_line = sp_lineno; \ 37 | } 38 | 39 | 40 | 41 | %} 42 | 43 | %option yylineno bison-bridge bison-locations 44 | 45 | ws [ \t] 46 | nl [\r\n] 47 | comment #[^\n]* 48 | 49 | hex_digit [0-9a-fA-F] 50 | hex_number 0x{hex_digit}+ 51 | dec_number [0-9]+ 52 | 53 | identifier [a-zA-Z][a-zA-Z0-9_]* 54 | byte Byte(\[|{ws}) 55 | bit Bit(\[|{ws}) 56 | br [\[\]\{\}] 57 | 58 | %% 59 | 60 | 61 | {identifier} { 62 | sp_lval.string = strdup(sp_text); 63 | return IDENTIFIER; 64 | } 65 | {dec_number} { 66 | sp_lval.string = strdup(sp_text); 67 | return DEC_NUM; 68 | } 69 | {hex_number} { 70 | sp_lval.string = strdup(sp_text); 71 | return HEX_NUM; 72 | } 73 | 74 | {byte} { yyless(sp_leng - 1); return BYTE; } 75 | {bit} { yyless(sp_leng - 1); return BIT; } 76 | {br}|[\.-] { return sp_text[0]; } 77 | 78 | {comment} {} // eat comments 79 | {ws}+ {} // eat white space 80 | {nl}+ { colnum = 0; } // eat new lines and reset column 81 | 82 | . { 83 | printf("Line: %d - Unrecognized input: %s\n", sp_lineno, sp_text); 84 | return 0; 85 | } 86 | 87 | %% 88 | 89 | // Report error, reset the line numbering and flush buffer 90 | void sp_error(const char* s, ...) { 91 | va_list ap; 92 | va_start(ap, s); 93 | sp_lerror(sp_lloc, s, ap); 94 | sp_lineno = 1; 95 | YY_FLUSH_BUFFER; 96 | } 97 | 98 | // Reset the line numbering and flush the buffer at the end of a file 99 | int sp_wrap() { 100 | sp_lineno = 1; 101 | YY_FLUSH_BUFFER; 102 | return 1; 103 | } 104 | -------------------------------------------------------------------------------- /tag.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "mifare.h" 24 | #include "util.h" 25 | #include "tag.h" 26 | 27 | mf_tag_t current_tag; 28 | mf_tag_t current_auth; 29 | 30 | void strip_non_auth_data(mf_tag_t* tag); 31 | int load_mfd(const char* fn, mf_tag_t* tag); 32 | int save_mfd(const char* fn, const mf_tag_t* tag); 33 | 34 | int load_mfd(const char* fn, mf_tag_t* tag) { 35 | FILE* mfd_file = fopen(fn, "rb"); 36 | 37 | if (mfd_file == NULL) { 38 | printf("Could not open file: %s\n", fn); 39 | return 1; 40 | } 41 | 42 | if (fread(tag, 1, sizeof(mf_tag_t), mfd_file) != sizeof(mf_tag_t)) { 43 | printf("Could not read file: %s\n", fn); 44 | fclose(mfd_file); 45 | return 1; 46 | } 47 | 48 | fclose(mfd_file); 49 | return 0; 50 | } 51 | 52 | int save_mfd(const char* fn, const mf_tag_t* tag) { 53 | FILE* mfd_file = fopen(fn, "w"); 54 | 55 | if (mfd_file == NULL) { 56 | printf("Could not open file for writing: %s\n", fn); 57 | return 1; 58 | } 59 | 60 | if (fwrite(tag, 1, sizeof(mf_tag_t), mfd_file) != sizeof(mf_tag_t)) { 61 | printf("Could not write file: %s\n", fn); 62 | fclose(mfd_file); 63 | return 1; 64 | } 65 | 66 | fclose(mfd_file); 67 | return 0; 68 | } 69 | 70 | int load_tag(const char* fn) { 71 | return load_mfd(fn, ¤t_tag); 72 | } 73 | 74 | int save_tag(const char* fn) { 75 | return save_mfd(fn, ¤t_tag); 76 | } 77 | 78 | int load_auth(const char* fn) { 79 | if (load_mfd(fn, ¤t_auth)) 80 | return 1; 81 | 82 | strip_non_auth_data(¤t_auth); 83 | return 0; 84 | } 85 | 86 | int save_auth(const char* fn) { 87 | return save_mfd(fn, ¤t_auth); 88 | } 89 | 90 | 91 | int import_auth() { 92 | memcpy(¤t_auth, ¤t_tag, sizeof(mf_tag_t)); 93 | strip_non_auth_data(¤t_auth); 94 | return 0; 95 | } 96 | 97 | void print_tag(mf_size_t size) { 98 | if (size == MF_1K) 99 | print_tag_block_range(0, MF_1K / sizeof(mf_block_t) - 1); 100 | else if (size == MF_4K) 101 | print_tag_block_range(0, MF_4K / sizeof(mf_block_t) - 1); 102 | else { 103 | printf("Unsupported tag size.\n"); 104 | } 105 | return; 106 | } 107 | 108 | void print_tag_head() { 109 | print_tag_block_range(0, 3); 110 | return; 111 | } 112 | 113 | void print_tag_byte_bits(size_t byte, size_t first_bit, size_t last_bit) { 114 | 115 | // The byte to show parts of 116 | uint8_t data = current_tag.amb[byte / 16].mbd.abtData[byte % 16]; 117 | 118 | printf("["); 119 | 120 | for (size_t i = 0; i < 8; ++i) { 121 | // Separate nibbles 122 | if (i == 4) 123 | printf(" "); 124 | 125 | // Outside mask 126 | if (i < first_bit || i > last_bit) { 127 | printf("-"); 128 | continue; 129 | } 130 | 131 | // Inside mask 132 | if ((1< 15) 157 | block_last = 15; 158 | print_hex_array_sep(block_data + block_offset, 159 | block_last - block_offset + 1, " "); 160 | 161 | // Fill up end with spaces 162 | for (size_t i = block_last; i < 15; ++i) 163 | printf("-- "); 164 | 165 | // Finish of with a nl 166 | printf("\n"); 167 | 168 | first_byte += block_last - block_offset + 1; 169 | } 170 | } 171 | 172 | void print_tag_data_range(size_t byte_offset, size_t bit_offset, 173 | size_t byte_len, size_t bit_len) { 174 | 175 | printf("Offset: [%zu, %zu] Length: [%zu, %zu]\n", 176 | byte_offset, bit_offset, byte_len, bit_len); 177 | 178 | // Print partial first byte 179 | if (bit_offset) { 180 | size_t total_bits = byte_len * 8 + bit_len; 181 | size_t last_bit = bit_offset + total_bits - 1; 182 | if (last_bit > 7) 183 | last_bit = 7; 184 | 185 | print_tag_byte_bits(byte_offset, bit_offset, last_bit); 186 | printf("\n"); 187 | 188 | total_bits -= last_bit - bit_offset + 1; 189 | 190 | // Update data to be printed 191 | byte_offset++; 192 | bit_offset = 0; 193 | byte_len = total_bits / 8; 194 | bit_len = total_bits % 8; 195 | } 196 | 197 | // Print bytes 198 | if (byte_len) { 199 | print_tag_bytes(byte_offset, byte_offset + byte_len - 1); 200 | 201 | // Update data to be printed 202 | byte_offset += byte_len; 203 | byte_len = 0; 204 | } 205 | 206 | // Print trailing bits 207 | if (bit_len) { 208 | print_tag_byte_bits(byte_offset, 0, bit_len); 209 | printf("\n"); 210 | } 211 | } 212 | 213 | 214 | void print_tag_block_range(size_t first, size_t last) { 215 | 216 | // Print header 217 | printf("xS xB 00 07 08 0f ASCII \n"); 218 | printf("---------------------------------------------------------------------------\n"); 219 | 220 | // Iterate over all blocks 221 | for (size_t block = first; block <= last; ++block) { 222 | 223 | // Sector number 224 | printf("%02zx ", 225 | block < 0x10*4 ? block / 4 : 0x10 + (block - 0x10*4) / 0x10); 226 | 227 | // Block number 228 | printf("%02zx ", block); 229 | 230 | // then print the block data 231 | print_hex_array_sep(current_tag.amb[block].mbd.abtData, 232 | sizeof(mf_block_t), " "); 233 | 234 | // finally, an ascii rendering 235 | printf(" ["); 236 | print_ascii_rendering(current_tag.amb[block].mbd.abtData, 237 | sizeof(mf_block_t), '.'); 238 | printf("]"); 239 | 240 | // EOL 241 | printf("\n"); 242 | 243 | // Indicate sector bondaries with extra nl 244 | if (block < last && block < 16*4 && (block + 1) % 4 == 0) 245 | printf("\n"); 246 | else if (block < last && block > 16*4 && (block + 1) % 16 == 0) 247 | printf("\n"); 248 | } 249 | } 250 | 251 | void print_keys(const mf_tag_t* tag, mf_size_t size) { 252 | printf("xS xB KeyA KeyB\n"); 253 | printf("----------------------------------\n"); 254 | for (int block = 3; block < 0x10 * 4; block += 4) { 255 | printf("%02x %02x ", block / 4, block); 256 | print_hex_array(tag->amb[block].mbt.abtKeyA, 6); 257 | printf(" "); 258 | print_hex_array(tag->amb[block].mbt.abtKeyB, 6); 259 | printf("\n"); 260 | } 261 | 262 | if (size == MF_1K) 263 | return; 264 | 265 | printf("\n"); 266 | 267 | for (int block = 0xf; block < 0x0c * 0x10; block += 0x10) { 268 | printf("%02x %02x ", 0x10 + block/0x10, 0x10*4 + block); 269 | print_hex_array(tag->amb[0x10*4 + block].mbt.abtKeyA, 6); 270 | printf(" "); 271 | print_hex_array(tag->amb[0x10*4 + block].mbt.abtKeyB, 6); 272 | printf("\n"); 273 | } 274 | } 275 | 276 | void print_ac(const mf_tag_t* tag) { 277 | 278 | static const char* ac_data_str[8] = { 279 | /* 0 0 0 */ " A|B A|B A|B A|B . . . . . .", 280 | /* 0 0 1 */ " A|B x x A|B . . . . . .", 281 | /* 0 1 0 */ " A|B x x x . . . . . .", 282 | /* 0 1 1 */ " B B x x . . . . . .", 283 | /* 1 0 0 */ " A|B B x x . . . . . .", 284 | /* 1 0 1 */ " B x x x . . . . . .", 285 | /* 1 1 0 */ " A|B B B A|B . . . . . .", 286 | /* 1 1 1 */ " x x x x . . . . . .", 287 | }; 288 | 289 | static const char* ac_trailer_str[8] = { 290 | /* 0 0 0 */ " . . . . x A A x A A", 291 | /* 0 0 1 */ " . . . . x A A A A A", 292 | /* 0 1 0 */ " . . . . x x A x A x", 293 | /* 0 1 1 */ " . . . . x B A|B B x B", 294 | /* 1 0 0 */ " . . . . x B A|B x x B", 295 | /* 1 0 1 */ " . . . . x x A|B B x x", 296 | /* 1 1 0 */ " . . . . x x A|B x x x", 297 | /* 1 1 1 */ " . . . . x x A|B x x x", 298 | }; 299 | 300 | // Print header 301 | printf("xS xB Raw C1 C2 C3 R W I D AR AW ACR ACW BR BW\n"); 302 | printf("--------------------------------------------------------------------\n"); 303 | 304 | // Iterate over all blocks (in 1k sectors) 305 | for (size_t block = 0; block < 0x10 * 4; ++block) { 306 | 307 | // Sector number 308 | printf("%02zx ", 309 | block < 0x10*4 ? block / 4 : 0x10 + (block - 0x10*4) / 0x10); 310 | 311 | // Block number 312 | printf("%02zx ", block); 313 | 314 | const uint8_t* ac = tag->amb[block_to_trailer(block)].mbt.abtAccessBits; 315 | 316 | // Print raw bytes 317 | print_hex_array(ac, 4); 318 | 319 | // Print the C1, C2, C3 bits 320 | int c1 = (ac[1] & 1<<(4 + (block % 4))) > 0; 321 | int c2 = (ac[2] & 1<<(0 + (block % 4))) > 0; 322 | int c3 = (ac[2] & 1<<(4 + (block % 4))) > 0; 323 | printf(" %d %d %d", c1, c2, c3); 324 | 325 | // Print enterpretation 326 | int c123 = (c1<<2) | (c2<<1) | c3; 327 | if (block % 4 < 3) { 328 | // Data block 329 | printf("%s", ac_data_str[c123]); 330 | } 331 | else { 332 | // Trailer block 333 | printf("%s", ac_trailer_str[c123]); 334 | } 335 | 336 | printf("\n"); 337 | 338 | // Indicate sector bondaries with extra nl 339 | if ((block + 1) % 4 == 0) 340 | printf("\n"); 341 | } 342 | } 343 | 344 | 345 | const char* sprint_key(const uint8_t* key) { 346 | static char str_buff[13]; 347 | 348 | if (!key) 349 | return NULL; 350 | 351 | sprintf(str_buff, "%02x%02x%02x%02x%02x%02x", 352 | (unsigned int)(key[0]), 353 | (unsigned int)(key[1]), 354 | (unsigned int)(key[2]), 355 | (unsigned int)(key[3]), 356 | (unsigned int)(key[4]), 357 | (unsigned int)(key[5])); 358 | 359 | return str_buff; 360 | } 361 | 362 | // Return a string describing the tag type 1k|4k 363 | const char* sprint_size(mf_size_t size) { 364 | static const char* str_1k = "1k"; 365 | static const char* str_4k = "4k"; 366 | 367 | if (size == MF_1K) 368 | return str_1k; 369 | 370 | if (size == MF_4K) 371 | return str_4k; 372 | 373 | return NULL; 374 | } 375 | 376 | 377 | uint8_t* read_key(uint8_t* key, const char* str) { 378 | if (!key || !str) 379 | return NULL; 380 | 381 | static char byte_tok[] = {0, 0, 0}; 382 | char* byte_tok_end; 383 | for (int i = 0; i < 6; ++i) { 384 | byte_tok[0] = str[i*2]; 385 | byte_tok[1] = str[i*2+1]; 386 | key[i] = (uint8_t)strtol(byte_tok, &byte_tok_end, 16); 387 | if (*byte_tok_end != '\0') { 388 | return NULL; 389 | } 390 | } 391 | 392 | return key; 393 | } 394 | 395 | void clear_tag(mf_tag_t* tag) { 396 | memset((void*)tag, 0x00, MF_4K); 397 | } 398 | 399 | void strip_non_auth_data(mf_tag_t* tag) { 400 | static const size_t bs = sizeof(mf_block_t); 401 | 402 | // Clear 1k sector data 16 á 4 - only keep sector trailer 403 | for (size_t i = 0; i < 0x10; ++i) 404 | memset(((void*)tag) + i * 4 * bs, 0x00, 3 * bs); 405 | 406 | // Clear 2-4k sector data 12 á 16 - only keep sector trailer 407 | for (size_t i = 0; i < 0x0c; ++i) 408 | memset(((void*)tag) + 0x10 * 4 * bs + i * 0x10 * bs, 0x00, 0x0f * bs); 409 | } 410 | 411 | 412 | size_t block_count(mf_size_t size) { 413 | return size / 0x10; 414 | } 415 | 416 | size_t sector_count(mf_size_t size) { 417 | return size == MF_1K ? 0x10 : 0x1c; 418 | } 419 | 420 | int is_trailer_block(size_t block) { 421 | return (block + 1) % (block < 0x80 ? 4 : 0x10) == 0; 422 | } 423 | 424 | size_t block_to_sector(size_t block) { 425 | if (block < 0x10*4) 426 | return block / 4; 427 | 428 | return 0x10 + (block - 0x10*4) / 0x10; 429 | } 430 | 431 | size_t block_to_header(size_t block) { 432 | if (block < 0x10*4) 433 | return block + (block % 4); 434 | 435 | return block + (block % 0x10); 436 | } 437 | 438 | // Return the trailer block for the specified block 439 | size_t block_to_trailer(size_t block) 440 | { 441 | if (block < 0x10*4) 442 | return block + (3 - (block % 4)); 443 | 444 | return block + (0xf - (block % 0x10)); 445 | } 446 | 447 | // Return the trailer block for the specified sector 448 | size_t sector_to_trailer(size_t sector) { 449 | if (sector < 0x10) 450 | return sector * 4 + 3; 451 | else 452 | return 0x10 * 4 + (sector - 0x10) * 0x10 + 0xf; 453 | } 454 | 455 | // Return the sector size (in blocks) that contains the block 456 | size_t sector_size(size_t block) { 457 | return block < 0x10*4 ? 4 : 16; 458 | } 459 | 460 | // Extract the key for the block parameters sector of the tag and return it 461 | uint8_t* key_from_tag(const mf_tag_t* tag, 462 | mf_key_type_t key_type, 463 | size_t block) { 464 | 465 | static uint8_t key[6]; 466 | 467 | size_t trailer_block = block_to_trailer(block); 468 | 469 | if (key_type == MF_KEY_A) 470 | memcpy(key, tag->amb[trailer_block].mbt.abtKeyA, 6); 471 | else 472 | memcpy(key, tag->amb[trailer_block].mbt.abtKeyB, 6); 473 | 474 | return key; 475 | } 476 | 477 | // Write key to the sector of a tag, where the sector is specified by 478 | // the block. 479 | void key_to_tag(mf_tag_t* tag, const uint8_t* key, 480 | mf_key_type_t key_type, size_t block) { 481 | 482 | size_t trailer_block = block_to_trailer(block); 483 | 484 | if (key_type == MF_KEY_A) 485 | memcpy(tag->amb[trailer_block].mbt.abtKeyA, key, 6); 486 | else 487 | memcpy(tag->amb[trailer_block].mbt.abtKeyB, key, 6); 488 | } 489 | 490 | /** 491 | * Return block index of the first block in every sector in turn on 492 | * repeated calls. Initialize the iterator by calling with state 493 | * 0. Subsequent calls should use the tag size as state. The iterator 494 | * returns -1 as an end marker. 495 | */ 496 | int sector_header_iterator(int state) { 497 | static int block; 498 | 499 | if (state == 0) 500 | return block = 0; 501 | 502 | if (block + 4 < 0x10*4) 503 | return block += 4; 504 | 505 | if (state == MF_1K) // End marker for 1k state 506 | return -1; 507 | 508 | if (block + 0x10 < 0x100) 509 | return block += 0x10; 510 | 511 | return -1; // End marker for 4k state 512 | } 513 | -------------------------------------------------------------------------------- /tag.h: -------------------------------------------------------------------------------- 1 | #ifndef TAG__H 2 | #define TAG__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | * 22 | * Parts of code used in this file are from the GNU readline library file 23 | * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc 24 | */ 25 | 26 | #include "mifare.h" 27 | 28 | typedef enum { 29 | MF_INVALID_SIZE = 0, 30 | MF_1K = 1024, 31 | MF_4K = 4096 32 | } mf_size_t; 33 | 34 | typedef enum { 35 | MF_INVALID_KEY_TYPE = 0, 36 | MF_KEY_A = 'a', 37 | MF_KEY_B = 'b', 38 | MF_KEY_UNLOCKED = 0xff, 39 | } mf_key_type_t; 40 | 41 | // Convenience typedefs (shortening) 42 | typedef mifare_classic_tag mf_tag_t; 43 | typedef mifare_classic_block mf_block_t; 44 | 45 | // The active tag 46 | extern mf_tag_t current_tag; 47 | 48 | // The ACL + keys used 49 | extern mf_tag_t current_auth; 50 | 51 | // Load/Save tag or keys from file 52 | int load_tag(const char* fn); 53 | int load_auth(const char* fn); 54 | int save_tag(const char* fn); 55 | int save_auth(const char* fn); 56 | 57 | // Copy key data from the 'current_tag' to the 'current_auth' 58 | int import_auth(); 59 | 60 | // Output tag data 61 | void print_tag(); 62 | void print_tag_head(); 63 | void print_tag_block_range(size_t first, size_t last); 64 | void print_tag_data_range(size_t byte_offset, size_t bit_offset, 65 | size_t byte_len, size_t bit_len); 66 | void print_tag_bytes(size_t first_byte, size_t last_byte); 67 | 68 | void print_keys(const mf_tag_t* tag, mf_size_t size); 69 | void print_ac(const mf_tag_t* tag); 70 | 71 | // Return a hex string representationon of the key 72 | const char* sprint_key(const uint8_t* key); 73 | 74 | // Parse the string and set the key. Return the key, or NULL on error. 75 | uint8_t* read_key(uint8_t* key, const char* str); 76 | 77 | // Return a string describing the tag type 1k|4k 78 | const char* sprint_size(mf_size_t size); 79 | 80 | // Set the contents of a tag to zeroes 81 | void clear_tag(mf_tag_t* tag); 82 | 83 | // Return number of blocks for size 84 | size_t block_count(mf_size_t size); 85 | 86 | // Return number of sectors for size 87 | size_t sector_count(mf_size_t size); 88 | 89 | // Return > 0 if the block is a trailer, 0 otherwise. 90 | int is_trailer_block(size_t block); 91 | 92 | // Return the sector index of the block 93 | size_t block_to_sector(size_t block); 94 | 95 | // Return the head block for the specified block 96 | size_t block_to_header(size_t block); 97 | 98 | // Return the trailer block for the specified block 99 | size_t block_to_trailer(size_t block); 100 | 101 | // Return the trailer block for the specified sector 102 | size_t sector_to_trailer(size_t sector); 103 | 104 | // Return the sector size (in blocks) that contains the block 105 | size_t sector_size(size_t block); 106 | 107 | // Extract the key for the block parameters sector of the tag and return it 108 | uint8_t* key_from_tag(const mf_tag_t* tag, 109 | mf_key_type_t key_type, 110 | size_t block); 111 | 112 | // Write key to the sector of a tag, where the sector is specified by 113 | // the block (anywhere in the sector). 114 | void key_to_tag(mf_tag_t* tag, const uint8_t* key, 115 | mf_key_type_t key_type, size_t block); 116 | 117 | /** 118 | * Return block index of the first block in every sector in turn on 119 | * repeated calls. Initialize the iterator by calling with state 120 | * 0. Subsequent calls should use the tag size as state. The iterator 121 | * returns -1 as an end marker. 122 | */ 123 | int sector_header_iterator(int state); 124 | 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /term_cmd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | * 19 | * Parts of code used in this file are from the GNU readline library file 20 | * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "mfterm.h" 28 | #include "tag.h" 29 | #include "term_cmd.h" 30 | #include "mifare_ctrl.h" 31 | #include "dictionary.h" 32 | #include "spec_syntax.h" 33 | #include "util.h" 34 | #include "mac.h" 35 | 36 | command_t commands[] = { 37 | { "help", com_help, 0, 0, "Display this text" }, 38 | { "?", com_help, 0, 0, "Synonym for 'help'" }, 39 | 40 | { "quit", com_quit, 0, 1, "Exit the program" }, 41 | { "q", com_quit, 0, 0, "Exit the program" }, 42 | { "exit", com_quit, 0, 0, "Synonym for 'quit'" }, 43 | 44 | { "load", com_load_tag, 1, 1, "Load tag data from a file" }, 45 | { "save", com_save_tag, 1, 1, "Save tag data to a file" }, 46 | { "clear", com_clear_tag, 0, 1, "Clear the current tag data" }, 47 | 48 | { "read", com_read_tag, 0, 1, "A|B : Read tag data from a physical tag" }, 49 | { "read unlocked", com_read_tag_unlocked, 0, 1, "On pirate cards, read card without keys" }, 50 | { "write", com_write_tag, 0, 1, "A|B : Write tag data to a physical tag" }, 51 | { "write unlocked", com_write_tag_unlocked, 0, 1, "On pirate cards, write 1k tag with block 0" }, 52 | 53 | { "print", com_print, 0, 1, "1k|4k : Print tag data" }, 54 | { "p", com_print, 0, 0, "1k|4k : Print tag data" }, 55 | { "print head", com_print_head, 0, 1, "Print first sector" }, 56 | { "print keys", com_print_keys, 0, 1, "1k|4k : Print tag's keys" }, 57 | { "print ac", com_print_ac, 0, 1, "Print access conditions" }, 58 | 59 | { "set", com_set, 0, 1, "#block #offset = xx xx xx : Set tag data" }, 60 | { "setuid", com_setuid, 0, 1, "xx xx xx xx: Set tag UID" }, 61 | 62 | { "keys load", com_keys_load, 1, 1, "Load keys from a file" }, 63 | { "keys save", com_keys_save, 1, 1, "Save keys to a file" }, 64 | { "keys clear", com_keys_clear, 0, 1, "Clear the keys" }, 65 | { "keys set", com_keys_set, 0, 1, "A|B #S key : Set a key value" }, 66 | { "keys import", com_keys_import, 0, 1, "Import keys from the current tag" }, 67 | { "keys test", com_keys_test, 0, 1, "Try to authenticate with the keys" }, 68 | { "keys", com_keys_print, 0, 1, "1k|4k : Print the keys" }, 69 | 70 | { "dict load", com_dict_load, 1, 1, "Load a dictionary key file" }, 71 | { "dict clear", com_dict_clear, 0, 1, "Clear the key dictionary" }, 72 | { "dict attack", com_dict_attack, 0, 1, "Find keys of a physical tag"}, 73 | { "dict", com_dict_print, 0, 1, "Print the key dictionary" }, 74 | 75 | { "spec load", com_spec_load, 1, 1, "Load a specification file" }, 76 | { "spec clear", com_spec_clear, 0, 1, "Unload the specification" }, 77 | { "spec", com_spec_print, 0, 1, "Print the specification" }, 78 | 79 | { "mac key", com_mac_key_get_set, 0, 1, " : Get or set MAC key" }, 80 | { "mac compute", com_mac_block_compute, 0, 1, "#block : Compute block MAC" }, 81 | { "mac update", com_mac_block_update, 0, 1, "#block : Compute block MAC" }, 82 | { "mac validate", com_mac_validate, 0, 1, "1k|4k : Validates block MAC of the whole tag" }, 83 | 84 | { (char *)NULL, (cmd_func_t)NULL, 0, 0, (char *)NULL } 85 | }; 86 | 87 | // Parse a Mifare size type argument (1k|4k) 88 | mf_size_t parse_size(const char* str); 89 | 90 | // Parse a Mifare size type argument (1k|4k). Return the default 91 | // argument value if the string is NULL. 92 | mf_size_t parse_size_default(const char* str, mf_size_t default_size); 93 | 94 | // Parse a Mifare key type argument (A|B) 95 | mf_key_type_t parse_key_type(const char* str); 96 | 97 | // Parse a Mifare key type argument (A|B). Return the default 98 | // argument value if the string is NULL. 99 | mf_key_type_t parse_key_type_default(const char* str, 100 | mf_key_type_t default_type); 101 | 102 | // Compute the MAC using the current_mac_key. If update is nonzero, 103 | // the mac of the current tag is updated. If not, the MAC is simply 104 | // printed. 105 | int com_mac_block_compute_impl(char* arg, int update); 106 | 107 | /* Look up NAME as the name of a command, and return a pointer to that 108 | command. Return a NULL pointer if NAME isn't a command name. */ 109 | command_t* find_command(const char *name) { 110 | command_t* cmd = NULL; 111 | size_t cmd_len = 0; 112 | 113 | for (size_t i = 0; commands[i].name; ++i) { 114 | size_t l = strlen(commands[i].name); 115 | if (l > cmd_len && strncmp(name, commands[i].name, l) == 0) { 116 | cmd = &commands[i]; 117 | cmd_len = l; 118 | } 119 | } 120 | 121 | return cmd; 122 | } 123 | 124 | /** 125 | * Helper function to print the specified command alligned with the longest 126 | * command name. 127 | */ 128 | void print_help_(size_t cmd) { 129 | 130 | // Find longest command (and cache the result) 131 | static size_t cmd_len_max = 0; 132 | if (cmd_len_max == 0) { 133 | for (int i = 0; commands[i].name; ++i) { 134 | size_t cmd_len = strlen(commands[i].name); 135 | cmd_len_max = cmd_len > cmd_len_max ? cmd_len : cmd_len_max; 136 | } 137 | } 138 | 139 | // Format: 4x' ' | cmd | ' '-pad-to-longest-cmd | 4x' ' | doc 140 | printf (" %s", commands[cmd].name); 141 | for (int j = (int)(cmd_len_max - strlen(commands[cmd].name)); j >= 0; --j) 142 | printf(" "); 143 | printf (" %s.\n", commands[cmd].doc); 144 | } 145 | 146 | int com_help(char* arg) { 147 | 148 | // Help request for specific command? 149 | if (arg && *arg != '\0') { 150 | for (size_t i = 0; commands[i].name; ++i) { 151 | if (strcmp(arg, commands[i].name) == 0) { 152 | print_help_(i); 153 | return 0; 154 | } 155 | } 156 | printf ("No commands match '%s'\n", arg); 157 | } 158 | 159 | // Help for all commands (with doc flag) 160 | for (size_t i = 0; commands[i].name; i++) { 161 | if (commands[i].document) 162 | print_help_(i); 163 | } 164 | 165 | return 0; 166 | } 167 | 168 | int com_quit(char *arg) { 169 | stop_input_loop(); 170 | return 0; 171 | } 172 | 173 | int com_load_tag(char *arg) { 174 | int res = load_tag(arg); 175 | if (res == 0) 176 | printf("Successfully loaded tag from: %s\n", arg); 177 | return 0; 178 | } 179 | 180 | int com_save_tag(char* arg) { 181 | int res = save_tag(arg); 182 | if (res == 0) 183 | printf("Successfully wrote tag to: %s\n", arg); 184 | return 0; 185 | } 186 | 187 | int com_clear_tag(char* arg) { 188 | clear_tag(¤t_tag); 189 | return 0; 190 | } 191 | 192 | int com_read_tag(char* arg) { 193 | // Add option to choose key 194 | char* ab = strtok(arg, " "); 195 | 196 | if (ab && strtok(NULL, " ") != (char*)NULL) { 197 | printf("Too many arguments\n"); 198 | return -1; 199 | } 200 | if (!ab) 201 | printf("No key argument (A|B) given. Defaulting to A\n"); 202 | 203 | // Parse key selection 204 | mf_key_type_t key_type = parse_key_type_default(ab, MF_KEY_A); 205 | if (key_type == MF_INVALID_KEY_TYPE) { 206 | printf("Invalid argument (A|B): %s\n", ab); 207 | return -1; 208 | } 209 | 210 | // Issue the read request 211 | mf_read_tag(¤t_tag, key_type); 212 | return 0; 213 | } 214 | 215 | int com_read_tag_unlocked(char* arg) { 216 | char* ab = strtok(arg, " "); 217 | if (ab) { 218 | printf("This command doesn't take any arguments\n"); 219 | return -1; 220 | } 221 | 222 | // Issue the read request 223 | mf_read_tag(¤t_tag, MF_KEY_UNLOCKED); 224 | return 0; 225 | } 226 | 227 | int com_write_tag(char* arg) { 228 | // Add option to choose key 229 | char* ab = strtok(arg, " "); 230 | 231 | if (!ab) { 232 | printf("Too few arguments: (A|B)\n"); 233 | return -1; 234 | } 235 | 236 | if (strtok(NULL, " ") != (char*)NULL) { 237 | printf("Too many arguments\n"); 238 | return -1; 239 | } 240 | 241 | // Parse key selection 242 | mf_key_type_t key_type = parse_key_type(ab); 243 | if (key_type == MF_INVALID_KEY_TYPE) { 244 | printf("Invalid argument (A|B): %s\n", ab); 245 | return -1; 246 | } 247 | 248 | // Issue the read request 249 | mf_write_tag(¤t_tag, key_type); 250 | return 0; 251 | } 252 | 253 | int com_write_tag_unlocked(char* arg) { 254 | char* ab = strtok(arg, " "); 255 | if (ab) { 256 | printf("This command doesn't take any arguments\n"); 257 | return -1; 258 | } 259 | 260 | // Issue the write request 261 | mf_write_tag(¤t_tag, MF_KEY_UNLOCKED); 262 | return 0; 263 | } 264 | 265 | int com_print(char* arg) { 266 | 267 | char* a = strtok(arg, " "); 268 | 269 | if (a && strtok(NULL, " ") != (char*)NULL) { 270 | printf("Too many arguments\n"); 271 | return -1; 272 | } 273 | 274 | mf_size_t size = parse_size_default(a, MF_1K); 275 | 276 | if (size == MF_INVALID_SIZE) { 277 | printf("Unknown argument: %s\n", a); 278 | return -1; 279 | } 280 | 281 | print_tag(size); 282 | 283 | return 0; 284 | } 285 | 286 | int com_print_head(char* arg) { 287 | print_tag_head(); 288 | return 0; 289 | } 290 | 291 | int com_set(char* arg) { 292 | char* block_str = strtok(arg, " "); 293 | char* offset_str = strtok(NULL, " "); 294 | char* byte_str = strtok(NULL, " "); 295 | 296 | if (!block_str || !offset_str || !byte_str) { 297 | printf("Too few arguments: #block #offset xx xx xx .. xx\n"); 298 | return -1; 299 | } 300 | 301 | unsigned int block = (unsigned int) strtoul(block_str, &block_str, 16); 302 | if (*block_str != '\0') { 303 | printf("Invalid block character (non hex): %s\n", block_str); 304 | return -1; 305 | } 306 | if (block > 0xff) { 307 | printf("Invalid block [0,ff]: %x\n", block); 308 | return -1; 309 | } 310 | 311 | unsigned int offset = (unsigned int) strtoul(offset_str, &offset_str, 16); 312 | if (*offset_str != '\0') { 313 | printf("Invalid offset character (non hex): %s\n", offset_str); 314 | return -1; 315 | } 316 | if (offset > 0x0f) { 317 | printf("Invalid offset [0,f]: %x\n", offset); 318 | return -1; 319 | } 320 | 321 | // Consume the byte tokens 322 | do { 323 | long int byte = strtol(byte_str, &byte_str, 16); 324 | if (*byte_str != '\0') { 325 | printf("Invalid byte character (non hex): %s\n", byte_str); 326 | return -1; 327 | } 328 | if (byte < 0 || byte > 0xff) { 329 | printf("Invalid byte value [0,ff]: %lx\n", byte); 330 | return -1; 331 | } 332 | 333 | if (offset > 0x0f) { 334 | printf("Too many bytes specified.\n"); 335 | return -1; 336 | } 337 | 338 | // Write the data 339 | current_tag.amb[block].mbd.abtData[offset++] = (uint8_t)byte; 340 | 341 | } while((byte_str = strtok(NULL, " ")) != (char*)NULL); 342 | 343 | return 0; 344 | } 345 | 346 | int com_setuid(char* arg) { 347 | char* byte_str = strtok(arg, " "); 348 | int block = 0; 349 | 350 | /// TODO : Check arg size (display warning if < 4) 351 | if (!byte_str) { 352 | printf("Too few arguments: xx xx xx xx\n"); 353 | return -1; 354 | } 355 | 356 | // Consume the byte tokens 357 | do { 358 | long int byte = strtol(byte_str, &byte_str, 16); 359 | 360 | if (byte < 0 || byte > 0xff) { 361 | printf("Invalid byte value [0,ff]: %lx\n", byte); 362 | return -1; 363 | } 364 | 365 | // Write the data 366 | current_tag.amb[0].mbd.abtData[block++] = (uint8_t)byte; 367 | 368 | } while(((byte_str = strtok(NULL, " ")) != (char*)NULL) && (block < 4)); 369 | // Compute and write BCC 370 | current_tag.amb[0].mbd.abtData[4] = (uint8_t)current_tag.amb[0].mbd.abtData[0] ^ 371 | (uint8_t)current_tag.amb[0].mbd.abtData[1] ^ 372 | (uint8_t)current_tag.amb[0].mbd.abtData[2] ^ 373 | (uint8_t)current_tag.amb[0].mbd.abtData[3]; 374 | 375 | return 0; 376 | } 377 | 378 | int com_print_keys(char* arg) { 379 | char* a = strtok(arg, " "); 380 | 381 | if (a && strtok(NULL, " ") != (char*)NULL) { 382 | printf("Too many arguments\n"); 383 | return -1; 384 | } 385 | 386 | mf_size_t size = parse_size_default(a, MF_1K); 387 | 388 | if (size == MF_INVALID_SIZE) { 389 | printf("Unknown argument: %s\n", a); 390 | return -1; 391 | } 392 | 393 | print_keys(¤t_tag, size); 394 | 395 | return 0; 396 | } 397 | 398 | int com_print_ac(char* arg) { 399 | if (strtok(arg, " ") != (char*)NULL) { 400 | printf("Too many arguments\n"); 401 | return -1; 402 | } 403 | 404 | print_ac(¤t_tag); 405 | 406 | return 0; 407 | } 408 | 409 | int com_keys_load(char* arg) { 410 | int res = load_auth(arg); 411 | if (res == 0) 412 | printf("Successfully loaded keys from: %s\n", arg); 413 | return 0; 414 | } 415 | 416 | int com_keys_save(char* arg) { 417 | int res = save_auth(arg); 418 | if (res == 0) 419 | printf("Successfully wrote keys to: %s\n", arg); 420 | return 0; 421 | } 422 | 423 | int com_keys_clear(char* arg) { 424 | clear_tag(¤t_auth); 425 | return 0; 426 | } 427 | 428 | int com_keys_set(char* arg) { 429 | // Arg format: A|B #S key 430 | 431 | char* ab = strtok(arg, " "); 432 | char* sector_str = strtok(NULL, " "); 433 | char* key_str = strtok(NULL, " "); 434 | 435 | if (strtok(NULL, " ") != (char*)NULL) { 436 | printf("Too many arguments\n"); 437 | return -1; 438 | } 439 | 440 | if (!ab || !sector_str || !key_str) { 441 | printf("Too few arguments: (A|B) #sector key\n"); 442 | return -1; 443 | } 444 | 445 | // Read sector 446 | long int sector = strtol(sector_str, §or_str, 16); 447 | 448 | // Sanity check sector range 449 | if (*sector_str != '\0') { 450 | printf("Invalid sector character (non hex): %s\n", sector_str); 451 | return -1; 452 | } 453 | if (sector < 0 || sector > 0x1b) { 454 | printf("Invalid sector [0,1b]: %lx\n", sector); 455 | return -1; 456 | } 457 | 458 | // Sanity check key length 459 | if (strncmp(key_str, "0x", 2) == 0) 460 | key_str += 2; 461 | if (strlen(key_str) != 12) { 462 | printf("Invalid key (6 byte hex): %s\n", key_str); 463 | return -1; 464 | } 465 | 466 | // Compute the block that houses the key for the desired sector 467 | size_t block = sector_to_trailer((size_t)sector); 468 | 469 | // Parse key selection and point to appropriate key 470 | uint8_t* key; 471 | mf_key_type_t key_type = parse_key_type(ab); 472 | if (key_type == MF_KEY_A) 473 | key = current_auth.amb[block].mbt.abtKeyA; 474 | else if (key_type == MF_KEY_B) 475 | key = current_auth.amb[block].mbt.abtKeyB; 476 | else { 477 | printf("Invalid argument (A|B): %s\n", ab); 478 | return -1; 479 | } 480 | 481 | // Parse the key 482 | if (read_key(key, key_str) == NULL) { 483 | printf("Invalid key character (non hex)\n"); 484 | return -1; 485 | } 486 | 487 | return 0; 488 | } 489 | 490 | int com_keys_import(char* arg) { 491 | import_auth(); 492 | return 0; 493 | } 494 | 495 | int com_keys_test(char* arg) { 496 | // Arg format: 1k|4k A|B 497 | 498 | char* s = strtok(arg, " "); 499 | char* ab = strtok(NULL, " "); 500 | 501 | if (s && ab && strtok(NULL, " ") != NULL) { 502 | printf("Too many arguments\n"); 503 | return -1; 504 | } 505 | 506 | if (!s || !ab) { 507 | printf("Too few arguments: (1k|4k) (A|B)\n"); 508 | return -1; 509 | } 510 | 511 | // Parse arguments 512 | mf_size_t size = parse_size(s); 513 | if (size == MF_INVALID_SIZE) { 514 | printf("Unknown size argument (1k|4k): %s\n", s); 515 | return -1; 516 | } 517 | 518 | mf_key_type_t key_type = parse_key_type(ab); 519 | if (key_type == MF_INVALID_KEY_TYPE) { 520 | printf("Unknown key type argument (A|B): %s\n", ab); 521 | return -1; 522 | } 523 | 524 | // Run the auth test 525 | mf_test_auth(¤t_auth, size, key_type); 526 | return 0; 527 | } 528 | 529 | int com_keys_print(char* arg) { 530 | char* a = strtok(arg, " "); 531 | 532 | if (a && strtok(NULL, " ") != (char*)NULL) { 533 | printf("Too many arguments\n"); 534 | return -1; 535 | } 536 | 537 | mf_size_t size = parse_size_default(a, MF_1K); 538 | 539 | if (size == MF_INVALID_SIZE) { 540 | printf("Unknown argument: %s\n", a); 541 | return -1; 542 | } 543 | 544 | print_keys(¤t_auth, size); 545 | 546 | return 0; 547 | } 548 | 549 | int com_dict_load(char* arg) { 550 | FILE* dict_file = fopen(arg, "r"); 551 | 552 | if (dict_file == NULL) { 553 | printf("Could not open file: %s\n", arg); 554 | return 1; 555 | } 556 | 557 | dictionary_import(dict_file); 558 | 559 | fclose(dict_file); 560 | return 0; 561 | } 562 | 563 | int com_dict_clear(char* arg) { 564 | dictionary_clear(); 565 | return 0; 566 | } 567 | 568 | int com_dict_attack(char* arg) { 569 | 570 | // Not much point if we don't have any keys 571 | if (!dictionary_get()) { 572 | printf("Dictionary is empty!\n"); 573 | return -1; 574 | } 575 | 576 | mf_dictionary_attack(¤t_auth); 577 | return 0; 578 | } 579 | 580 | int com_dict_print(char* arg) { 581 | key_list_t* kl = dictionary_get(); 582 | 583 | int count = 0; 584 | while(kl) { 585 | printf("%s\n", sprint_key(kl->key)); 586 | kl = kl->next; 587 | ++count; 588 | } 589 | 590 | printf("Dictionary contains: %d keys\n", count); 591 | 592 | return 0; 593 | } 594 | 595 | 596 | int com_spec_print(char* arg) { 597 | print_instance_tree(); 598 | 599 | return 0; 600 | } 601 | 602 | int com_spec_load(char* arg) { 603 | // Start by clearing the current hierarcy 604 | clear_instance_tree(); 605 | tt_clear(); 606 | 607 | // Open the file 608 | FILE* spec_file = fopen(arg, "r"); 609 | if (spec_file == NULL) { 610 | printf("Could not open file: %s\n", arg); 611 | return 1; 612 | } 613 | 614 | // Parse the specification 615 | spec_import(spec_file); 616 | 617 | fclose(spec_file); 618 | 619 | return 0; 620 | } 621 | 622 | int com_spec_clear(char* arg) { 623 | 624 | clear_instance_tree(); 625 | tt_clear(); 626 | 627 | return 0; 628 | } 629 | 630 | int com_mac_key_get_set(char* arg) { 631 | char* key_str = strtok(arg, " "); 632 | 633 | if (key_str == 0) { 634 | printf("Current MAC key: \n"); 635 | print_hex_array_sep(current_mac_key, 8, " "); 636 | printf("\n"); 637 | return 0; 638 | } 639 | 640 | uint8_t key[8]; 641 | int key_ptr = 0; 642 | 643 | // Consume the key tokens 644 | do { 645 | long int byte = strtol(key_str, &key_str, 16); 646 | if (*key_str != '\0') { 647 | printf("Invalid key character (non hex): %s\n", key_str); 648 | return -1; 649 | } 650 | if (byte < 0 || byte > 0xff) { 651 | printf("Invalid byte value [0,ff]: %lx\n", byte); 652 | return -1; 653 | } 654 | 655 | if (key_ptr > sizeof(key)) { 656 | printf("Too many bytes specified in key (should be 8).\n"); 657 | return -1; 658 | } 659 | 660 | // Accept the byte and add it to the key 661 | key[key_ptr++] = (uint8_t)byte; 662 | 663 | } while((key_str = strtok(NULL, " ")) != (char*)NULL); 664 | 665 | if (key_ptr != sizeof(key)) { 666 | printf("Too few bytes specified in key (should be 8).\n"); 667 | return -1; 668 | } 669 | 670 | // Everything ok, so update the global 671 | memcpy(current_mac_key, key, 8); 672 | return 0; 673 | } 674 | 675 | int com_mac_block_compute(char* arg) { 676 | return com_mac_block_compute_impl(arg, 0); 677 | } 678 | 679 | int com_mac_block_update(char* arg) { 680 | return com_mac_block_compute_impl(arg, 1); 681 | } 682 | 683 | int com_mac_block_compute_impl(char* arg, int update) { 684 | char* block_str = strtok(arg, " "); 685 | 686 | if (!block_str) { 687 | printf("Too few arguments: #block\n"); 688 | return -1; 689 | } 690 | 691 | unsigned int block = (unsigned int) strtoul(block_str, &block_str, 16); 692 | if (*block_str != '\0') { 693 | printf("Invalid block character (non hex): %s\n", block_str); 694 | return -1; 695 | } 696 | if (block > 0xff) { 697 | printf("Invalid block [0,ff]: %x\n", block); 698 | return -1; 699 | } 700 | 701 | // Use the key 702 | unsigned char* mac = compute_block_mac(block, current_mac_key, update); 703 | 704 | // MAC is null on error, else 8 bytes 705 | if (mac == 0) 706 | return -1; 707 | 708 | // Only need 16 MSBs. 709 | printf("Block %2.2x, MAC : ", block); 710 | print_hex_array_sep(mac, 2, " "); 711 | printf("\n"); 712 | 713 | return 0; 714 | } 715 | 716 | int com_mac_validate(char* arg) { 717 | char* a = strtok(arg, " "); 718 | 719 | if (a && strtok(NULL, " ") != (char*)NULL) { 720 | printf("Too many arguments\n"); 721 | return -1; 722 | } 723 | 724 | mf_size_t size = parse_size_default(a, MF_1K); 725 | 726 | if (size == MF_INVALID_SIZE) { 727 | printf("Unknown argument: %s\n", a); 728 | return -1; 729 | } 730 | 731 | for (unsigned int i = 1; i < block_count(size); ++i) { 732 | if (is_trailer_block(i)) 733 | continue; 734 | 735 | unsigned char* mac = compute_block_mac(i, current_mac_key, 0); 736 | printf("Block: %2x ", i); 737 | printf("Tag: "); 738 | print_hex_array_sep(¤t_tag.amb[i].mbd.abtData[14], 2, " "); 739 | printf(" Computed: "); 740 | print_hex_array_sep(mac, 2, " "); 741 | printf(" Result: "); 742 | 743 | if (memcmp(mac, ¤t_tag.amb[i].mbd.abtData[14], 2) == 0) 744 | printf("VALID"); 745 | else 746 | printf("IN-VALID"); 747 | 748 | printf("\n"); 749 | } 750 | return 0; 751 | } 752 | 753 | mf_size_t parse_size(const char* str) { 754 | 755 | if (str == NULL) 756 | return MF_INVALID_SIZE; 757 | 758 | if (strcasecmp(str, "1k") == 0) 759 | return MF_1K; 760 | 761 | if (strcasecmp(str, "4k") == 0) 762 | return MF_4K; 763 | 764 | return MF_INVALID_SIZE; 765 | } 766 | 767 | mf_size_t parse_size_default(const char* str, mf_size_t default_size) { 768 | if (str == NULL) 769 | return default_size; 770 | return parse_size(str); 771 | } 772 | 773 | mf_key_type_t parse_key_type(const char* str) { 774 | 775 | if (str == NULL) 776 | return MF_INVALID_KEY_TYPE; 777 | 778 | if (strcasecmp(str, "a") == 0) 779 | return MF_KEY_A; 780 | 781 | if (strcasecmp(str, "b") == 0) 782 | return MF_KEY_B; 783 | 784 | return MF_INVALID_KEY_TYPE; 785 | } 786 | 787 | mf_key_type_t parse_key_type_default(const char* str, 788 | mf_key_type_t default_type) { 789 | if (str == NULL) 790 | return default_type; 791 | return parse_key_type(str); 792 | } 793 | 794 | // Any command starting with '.' - path spec 795 | int exec_path_command(const char *line) { 796 | 797 | instance_t* inst = parse_spec_path(line); 798 | 799 | if (inst) 800 | print_tag_data_range(inst->offset_bytes, inst->offset_bits, 801 | inst->size_bytes, inst->size_bits); 802 | else 803 | printf("Invalid Path\n"); 804 | 805 | 806 | return 0; 807 | } 808 | -------------------------------------------------------------------------------- /term_cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef TERM_CMD__H 2 | #define TERM_CMD__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | */ 22 | 23 | typedef int (*cmd_func_t)(char*); 24 | 25 | // Print help command 26 | int com_help(char* arg); 27 | 28 | // Exit mfterm command 29 | int com_quit(char* arg); 30 | 31 | // Load/Save tag file operations 32 | int com_load_tag(char* arg); 33 | int com_save_tag(char* arg); 34 | 35 | // Clear (zero) tag command 36 | int com_clear_tag(char* arg); 37 | 38 | // Read/Write tag NFC operations 39 | int com_read_tag(char* arg); 40 | int com_read_tag_unlocked(char* arg); 41 | int com_write_tag(char* arg); 42 | int com_write_tag_unlocked(char* arg); 43 | 44 | // Tag print commands 45 | int com_print(char* arg); 46 | int com_print_head(char* arg); 47 | int com_print_keys(char* arg); 48 | int com_print_ac(char* arg); 49 | 50 | // Tag set (value) command 51 | int com_set(char* arg); 52 | int com_setuid(char* arg); 53 | 54 | // Key operations 55 | int com_keys_load(char* arg); 56 | int com_keys_save(char* arg); 57 | int com_keys_clear(char* arg); 58 | int com_keys_set(char* arg); 59 | int com_keys_import(char* arg); 60 | int com_keys_print(char* arg); 61 | int com_keys_test(char* arg); 62 | 63 | // Dictionary operations 64 | int com_dict_load(char* arg); 65 | int com_dict_clear(char* arg); 66 | int com_dict_attack(char* arg); 67 | int com_dict_print(char* arg); 68 | 69 | // Specification operations 70 | int com_spec_load(char* arg); 71 | int com_spec_clear(char* arg); 72 | int com_spec_print(char* arg); 73 | 74 | // MAC operations 75 | int com_mac_key_get_set(char* arg); 76 | int com_mac_block_compute(char* arg); 77 | int com_mac_block_update(char* arg); 78 | int com_mac_validate(char* arg); 79 | 80 | typedef struct { 81 | char *name; // The command 82 | cmd_func_t func; // Function to call on command 83 | int fn_arg; // File name completion if > 0 84 | int document; // Show in documentation if > 0 85 | char *doc; // String documenting the command 86 | } command_t; 87 | 88 | extern command_t commands[]; 89 | 90 | // Lookup a command by name. Return a ptr to the command function, or 91 | // NULL if the command isn't found. 92 | command_t* find_command(const char *name); 93 | 94 | // Any command starting with '.' - path spec 95 | int exec_path_command(const char *line); 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2011 Anders Sundman 3 | * 4 | * This file is part of mfterm. 5 | * 6 | * mfterm is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * mfterm is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with mfterm. If not, see . 18 | * 19 | * Parts of code used in this file are from the GNU readline library file 20 | * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "util.h" 27 | 28 | char* strdup(const char* string) { 29 | if (string == NULL) 30 | return NULL; 31 | 32 | // Allocate memory for the string + '\0' 33 | char* new_string = malloc(strlen(string) + 1); 34 | 35 | if(new_string) 36 | strcpy(new_string, string); 37 | 38 | return new_string; 39 | } 40 | 41 | int is_space(char c) { 42 | return c == ' ' || c == '\t'; 43 | } 44 | 45 | /* Strip whitespace from the start and end of STRING. Return a pointer 46 | into STRING. */ 47 | char* trim(char* string) { 48 | 49 | char* s = string; 50 | while (is_space(*s)) 51 | ++s; 52 | 53 | if (*s == 0) 54 | return s; 55 | 56 | char* t = s + strlen(s) - 1; 57 | while (t > s && is_space(*t)) 58 | --t; 59 | *++t = '\0'; 60 | 61 | return s; 62 | } 63 | 64 | void print_hex_array(const unsigned char* data, size_t nbytes) { 65 | print_hex_array_sep(data, nbytes, NULL); 66 | } 67 | 68 | void print_hex_array_sep(const unsigned char* data, size_t nbytes, char* sep) { 69 | for (int i = 0; i < nbytes; ++i) { 70 | printf("%02x", data[i]); 71 | if (sep) 72 | printf("%s", sep); 73 | } 74 | } 75 | 76 | void print_ascii_rendering(const unsigned char* data, size_t nbytes, char nonascii) { 77 | for (int i = 0; i < nbytes; ++i) { 78 | if (data[i] >= 32 && data[i] < 127) 79 | printf("%c", data[i]); 80 | else 81 | printf("%c", nonascii); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL__H 2 | #define UTIL__H 3 | 4 | /** 5 | * Copyright (C) 2011 Anders Sundman 6 | * 7 | * This file is part of mfterm. 8 | * 9 | * mfterm is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * mfterm is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with mfterm. If not, see . 21 | * 22 | * Parts of code used in this file are from the GNU readline library file 23 | * fileman.c (GPLv3). Copyright (C) 1987-2009 Free Software Foundation, Inc 24 | */ 25 | 26 | /* Strip whitespace from the start and end of STRING. Return a pointer 27 | into STRING. */ 28 | char* trim(char* string); 29 | 30 | // Allocate a new string and duplicate the argument string. 31 | char* strdup(const char* string); 32 | 33 | // Print a byte array in hex without byte separation 34 | void print_hex_array(const unsigned char* data, size_t nbytes); 35 | 36 | // Print a byte array in hex with the specified byte separation. 37 | void print_hex_array_sep(const unsigned char* data, size_t nbytes, char* sep); 38 | 39 | // Print binary data as ascii - replace non printable chars with nonascii 40 | void print_ascii_rendering(const unsigned char* data, size_t nbytes, char nonascii); 41 | 42 | #endif 43 | --------------------------------------------------------------------------------