[Из песочницы] Визуализация и декодирование данных с магнитных АТМ-карт

Предмет интереса этой публикации — считывание и декодирование данных со второй дорожки банкоматовской карточки в условиях дефицита оборудования и средств.Для начала приведу сухие теоретические знания. Если теория не интересует — можно пропустить.Пленка или дорожка представляет собой пластик, с нанесенными мельчайшими (очень мелкими) частицами ферромагнетика — вещества, обладающего остаточной намагниченностью. Ферромагнетик, кроме того, что он чертовски мелкий и не различим в школьный или ремонтный микроскоп, так он еще имеет форму, похожую на соты, гранулы или кристаллы.

В процессе нанесения на поверхность он сразу проходит ориентацию — то есть поворачивается согласно линиям магнитного поля. Этим достигается некоторое улучшение магнитных свойств дорожки. Дальше наносится фиксатор и защитный слой. Все, менять своего положения физически они не могут, изменение направлений векторов магнитных индукций (та самая остаточная намагниченность, магнитные линии) может происходить теперь только на молекулярном уровне. Но не нужно думать, что полоска после изготовления сразу обладает магнитными свойствами. Во-первых, если они есть, то слишком малы, во-вторых, направления векторов ориентированы случайным и компенсируют друг друга.

Вносим такую полоску в зону действия магнитного поля и ждем. Ждем, пока произойдет перепрыгивание энергетических лошадок через барьер и векторы магнитной индукции этих частичек не станут совпадать по величине с векторами внешнего магнитного поля, а сами частички не запасут некоторую энергию и не станут самыми настоящими постоянными магнитами. Всё, теперь этой полоской можно притягивать гвозди и шокировать обезьян в зоопарке.

Существует некое первичное намагничивание, переводящее девственную дорожку из состояния летаргии в состояние остаточной намагниченности. Для полного представления нужно рассмотреть картинку гистерезиса, она отражает все возможные магнитные состояния наших ферромагнетиков. Среди которых есть и два состояния полного размагничивания.

Теперь ближе к тому, как все происходит при записи и чтении на реальной магнитной полосе карты.

Первое. При записи головка, которая создает поле возле магнитной полосы, на самом деле создает поле не рядом с ней, а в ней. Чтобы это понять, нужно вспомнить простое правило: ток течет по пути наименьшего сопротивления. А теперь представим подкову, да, обычную подкову от лошади. Она имеет разрез, прислоните подкову разрывом перпендикулярно полосе. Это и есть современная головка для подобных записей.То есть все дело в разрыве, который почти прикасается к магнитной полосе.

Обмотаем подкову проволокой и пустим электрический ток, подкова станет электромагнитом, магнитное поле потечет по подкове и в месте разрыва подковы встретит сильное сопротивление — воздух или какой-то специально подсунутый туда диэлектрик.

Что остается делать магнитному полю? Пойти по пути наименьшего сопротивления — по самой магнитной полоске! Скакнув на полоску, замкнуться через нее. Что нам на руку.

Способ представления дискретной (считай в дальнейшем — цифровой информации) на такой полоске теоретически может сильно отличаться. Можно подставить себя на месте конструктора и попытаться промоделировать процессы записи и считывания. При этом в наших руках следующие козыри:

— информацию нужно записывать участками фиксированной длины; — записывать на полоску можно 2 типа участков, то есть с разными направлениями силовых линий магнитного поля (векторов магнитной индукции и кучи других величин, на что нам, впрочем, пофиг). Для простоты обозначим их как SN и NS; — записывать на полоску можно типы участков SN и NS, но любой из них может иметь различную степень намагниченности, то есть можно обозначить градацию степени намагниченности для любого из участков, например, договорившись с вами о том, что будем приписывать справа цифру от 0 до 9, выражающую степень намагниченности в возрастании; — считывание возможно при условии изменения магнитного потока головки, которая проходит рядом с намагниченной полоской. Меняющийся магнитный поток проходит через сердечник (всю туже саму подкову) и в результате на выводах обмотки, расположенной на сердечнике, появиться меняющееся напряжение.

Таким образом, менять оно может только при изменении магнитного потока. А он может меняться в трех случаях:

а) изменение направлений линий магнитного поля. То есть участки должны идти в таком порядке:(SN)(NS)(SN). В результате получим, грубо говоря, 2 раза пульсирующий ток; б) изменение интенсивности магнитный полей участков. Например, такая последовательность участков должна вызывать появление\изменение электрического тока в головке — (SN1)(SN2)(SN9)(SN3) (3 изменения в амплитуде эл.тока головки), а вот такая уже нет — (SN5)(SN5)(SN5)(SN5)(SN5); в) комбинация, а и б (SN5)(NS5)(NS6).

Вроде проблем нет, бери, используй любой из вариантов для представления нулей дискретных участков. Да, для записи все просто. Проблемы появляются при считывании. Один из основных факторов, который ставит палки в колеса — это скорость считывания, то есть скорость протяжки ленты под головкой. Изобретенные человеком устройства для ручного протаскивания карты через них не могут позволить себе точно распознать скорость без дополнительных ухищрений, с которой рука проводит карту (это возможно, но нужно делать дополнительную тактовую магнитную или иную линию). В банкоматах проще и достаточная точность может быть реализована.

Так в чем же проблема? Ну, разная скорость и фиг бы с ней.

А вы попробуйте, будучи простым рядовым устройством разберите следующую последовательность:(SN)(NS)(NS)(SN)(NS)? (последовательность 1)(1) (2) (3) (4) (5)

Что можно сказать об этой последовательности? Только то, что на вызовет ровно 3 раза (между участками 1 и 2, 3 и 4, 4 и 5) смену полярности электрического поля в головке.

Если принять самый первый приходящий в голову вариант кодирования 0 и 1 сменой полярности участков магнитного поля, то уверенно можно сказать, что SN — это 1, а NS — это 0, и получив скачок напряжения в сторону +, можно сказать, что произошел переход с 1 на 0. Значит, распознано 2 участка. А вот дальше, где 2 и 3 стоят рядом — (NS)(NS) никакого изменения не будет, но ведь известно, что на этом месте последнее, что было распознано по предыдущему скачку напряжения — это 0! Но попробуй, докажи сколько точно нулей на этом участке при разных скоростях движения, два или больше. Тое сть что последовательность 1 именно последовательность 1, а не, допустим, эта:(SN)(NS)(NS) (NS) (NS) (SN)(NS)? (последовательность 2)(1) (2) (3) (ошибка) (ошибка) (4) (5)

Между 3 и 4 все ровно — скачок напряжения происходит в сторону — и можно сказать, что 3 — это 0, а 4 — это 1. Выходов из сложившейся ситуации масса, от линии синхронизации до частотного кодирования. Которое и является основным в магнитных картах — это удвоение частоты F\2F.

(NS)(SN)(NS)(NS)(SN)(SN)(NS)(SN)(NS)(SN)(1)(0)(0)(1)(1)

Смысл, думается, ясен. Для кодирования 1 используется факт интерпретации скачков напряжения — удвоение частоты смены полярностей участков для кодирования одного элемента. Если присмотреться получше, то можно увидеть, что такое кодирование полностью устраняет недостатки кодирования, представленного выше при некоторых условиях.

Допустим, мы остановим карту на участке (NS)(NS). Можно подумать, что устройство собьется и неправильно посчитает число нулей, то есть примет (NS)(NS)(NS)(NS) за 00. На самом деле этого не произойдет, потому что при кодировании условием перехода на следующую группу участков является смена полярности, а ее не происходит и устройство просто ставит 0 и ждет смены полярности (ждет хоть до бесконечности) для того, чтобы вновь запустить свой счетчик — генератор для распознания следующей группы участков. Вопрос, как устройство определяет частоту появления смен полярности, конечно, интересный, но сейчас не нужный.

Лучше один раз увидеть, чем сто раз прочитать. Заглянем в содержимое дорожек магнитных карт и посмотрим, можно ли в домашних условиях однозначно понять, что же на них написано. Попутно получив некоторый инструмент для визуальной оценки состояния дорожек.

Опилки, посыпаемые на лист бумаги, под которым находится магнит, выстраиваются вдоль силовых линий. Нам же от них нужно, чтобы они липли к намагниченным участкам дорожек карты, предательских их обличая.

Обычные опилки или металлическая пыль не подойдут, слишком большие частицы. Предлагается использовать частицы тонера для картриджей принтеров. Они очень тщательно обработаны, однородны, и некоторые из них имеют в своем составе магнитные пигменты.

image

В оригиналах-источниках идеи предлагается использовать смесь тонера и крахмала, но это, видимо, по той причине, что идея оценивать состояние карточек с помощью тонера или специального спрея появилась давно, а тонеры тогда были специфичными. Поэтому, например, нам ровняться на Францию от 1999 года не стоит. Мы пойдем в современный магазин и приобретем тонер с магнитными пигментами (как показано на рисунке), желательно цветной.

Обсыпав карту и постучав ее об стол для стряхивания излишек тонера получаем в свое распоряжение этот вновь открывшийся мир. На полоске сразу видно все дефекты участков, получаемые картой. В некоторых бытовых случаях это может пригодиться.

Мы же должны продолжить. Весь интерес в том, чтобы прочитать содержимое карты имея под руками только фотоаппарат, лупу, или конечно же микроскоп. Как будет ясно дальше, для анализа второй дорожки из 3-ей достаточно фотоаппарата. На рисунках представлены фотографии со среднего цифрового фотоаппарата и с цифрового микроскопа.

image

Вся дорожка №2 (ABA) целиком.

image

Вся дорожка №2 (ABA) целиком с нанесенными в программе paint ориентирами.

a8a9f3a2520d4c92b4abf34c01d730de.bmpИ та же самая дорожка, чуть крупнее:

1e174c4dbbc3406fb0c78a6e81c7a06b.jpg

Для удобства, конечно же, использовать снимки с микроскопа.

Итак, анализ. Но сперва нужно обратиться к стандартам:

ISO 7810 Physical Characteristics of Credit.Card Size DocumentISO 7811–1 EmbossingISO 7811–2 Magnetic Stripe — Low CoercivityISO 7811–3 Location of Embossed CharactersISO 7811–4 Location of Tracks 1 and 2ISO 7811–5 Location of Track 3ISO 7811–6 Magnetic Stripe — High CoercivityISO 7813 Financial Transaction Cards

Далее в правой стороне документа смотрим сверху вниз: — Рисунок карты и ее размеры 3.375×2,125 дюймов, один дюйм=2,54 см, получается 85×54 мм. Совпадает с размерами исследуемой карты.— Строчка «Magnetic Stripe Encoding — Financial Transaction Cards» показывает расстояние от края карты в дюймах и характеристики дорожек (track) в виде своеобразной таблицы. Всего дорожек 3, каждая имеет свое название:

TRACK 1 IATATRACK 2 ABATRACK 3 THRIFT

Плотность записи RECORDING DENSITY (bits per inch) для каждой из дорожек.CHARACTER CONFIGURATION (including parity bit) «Устройство символа», по сути, число бит на байт, с учетом бита четности и INFORMATION CONTENT (including control characters) содержимое символов. Видно, что первая дорожка содержит как цифры, так и буквы, вторая и третья содержат только цифры — 40 цифровых символов.Ниже и слева расположены расшифровки форматов дорожек.

В теории, у нашей 2-ой дорожки ABA должно содержаться не более 75 бит на дюйм. То есть не более 3,375 (длина карты)*75(емкость на дюйм)=253,125 бит=253 бита на всю дорожку. Размер байта — 5 бит, кодировка только цифры, при этом получается 253/5=50 символов, но по стандарту их 40. Лишние — это последовательности для синхронизации в самом начале дорожке — нули.

5 битное кодирование расписано в ссылке ниже.

--Data Bits-- Parity b1 b2 b3 b4 b5 Character Function 0 0 0 0 1 0 (0H) Data 1 0 0 0 0 1 (1H) » 0 1 0 0 0 2 (2H) » 1 1 0 0 1 3 (3H) » 0 0 1 0 0 4 (4H) » 1 0 1 0 1 5 (5H) » 0 1 1 0 1 6 (6H) » 1 1 1 0 0 7 (7H) » 0 0 0 1 0 8 (8H) » 1 0 0 1 1 9 (9H) » 0 1 0 1 1: (AH) Control 1 1 0 1 0; (BH) Start Sentinel 0 0 1 1 1 < (CH) Control 1 0 1 1 0 = (DH) Field Separator 0 1 1 1 0 > (EH) Control 1 1 1 1 1? (FH) End Sentinel Что ж, чтобы начать, надо начать. Имея таблицу декодирования, напишем программу расшифровки онной. И да, нам же нужен дамп, полученный при визуализации, вон он (разумеется, чуть изменен, чтобы скрыть персональные данные, которых, кстати говоря, там кот наплакал):000000000000000000000000000000000000000110111111100001000010000100001010101000101011000010000100001010110101000010000100010000010100010000010001000001101001110010010101101010100010110010000010010110110011010110101001000001000010101010101100000000000000000000

Ну и сама программа:

.686; create 32 bit code .model flat, stdcall; 32 bit memory model option casemap: none; case sensitive include \masm32\include\windows.inc include \masm32\include\masm32.inc include \masm32\include\gdi32.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\shell32.inc includelib \masm32\lib\masm32.lib includelib \masm32\lib\gdi32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib .data? .data Handle_File_Input dd 0 Handle_File_Result dd 0

InputFile db «Content_track_2.txt», 0 ResultFile db «Result.txt», 0 flStr OFSTRUCT <> NBW dd? , 0 NBR dd?, 0

title_box db »[ABA] track-decoder», 0 ms1 db «Некорректный формат дорожки: положение стартовой последовательности [Start_Sentinel] не совпадает с положением первого единичного бита, встретившегося на дорожке.Разница составляет — Продолжить выполнение?», 0 ms2 db «Дорожка пустая-содержит только нули», 0 ms3 db «Отсутствует или невозможно открыть фаил Content_track_2.txt», 0 ms4 db «Выполнение закончено, результ в файле Result.txt.Можно повторить выполнение, при этом файл ввода будет считан заново, а строка в нем перевернута.», 0

bet db 254 dup (030h) en db 0ffh Start_Sentinel db »1010», 0ffh; обрамление строки

; bet db »00000000000000000000000000000000000000011011111110000100001000010000101010100010101100001000010000101011010100001000010001000001010001000001000100000» ; bey db »110100111001001010110101010001011001000001001011011001101011010100100000100001010101010110000000000000000»

result db 60 DUP (0)

val_0 db '.' ;00000 val_1 db 030h;00001 — анси val_2 db 038h;00010 — анси val_3 db '?' ;00011 val_4 db 034h;00100 — анси val_5 db '?' ;00101. val_6 db '?' ;00110 val_7 db '?' ;00111 val_8 db 032h;01000 — анси val_9 db '?' ;01001 val_10 db '?' ;01010 val_11 db '?' ;01011 val_12 db '?' ;01100 val_13 db 036h;01101 — анси val_14 db '?' ;01110 val_15 db '?' ;01111 val_16 db 031h;10000 — анси val_17 db '?' ;10001 val_18 db '?' ;10010 val_19 db 039h;10011 — анси val_20 db '?' ;10100 val_21 db 035h;10101 — анси val_22 db 'M' ;10110 — анси val_23 db '?' ;10111 val_24 db '?' ;11000 val_25 db 033h;11001 — анси val_26 db 'S' ;11010 — анси val_27 db '?' ;11011 val_28 db 037h;11100 — анси val_29 db '?' ;11101 val_30 db '?' ;11110 val_31 db 'E' ;11111 — анси

.code start: begin: push OF_READWRITE push offset flStr push offset InputFile call OpenFile cmp eax, 0ffffffffh; чето не то с файлом jz ext; пусть пользватель разберется и повторит при необходимости mov dword ptr [Handle_File_Input], eax push FILE_BEGIN push NULL push 0 push eax call SetFilePointer; установим указатель на начало файла push 0 push Handle_File_Input call GetFileSize mov ebx, 254 cmp eax, ebx; сравним на макс. возможный размер второй дорожки cmova eax, ebx; упрямо прочитаем только 254 символа push NULL push offset NBW push eax push offset bet push Handle_File_Input call ReadFile; читаем дами

lea esi, [en-1] ; один конец строки lea edi, bet; второй конец строки reverse: cmp esi, edi jbe end_reverse mov al, byte ptr [esi] mov ah, byte ptr [edi] mov byte ptr [edi], al mov byte ptr [esi], ah dec esi inc edi jmp reverse end_reverse:

;------ Поиск Start_Sentinel------------------------------------------- mov edi, offset bet mov al, 030h mov ecx, 0ffh repz scasb; просканируем строку до первого знака »1» mov edx, edi xor edx, offset [Start_Sentinel+1] ; если результат нулевой и они равны то значит на дорожке одни нули jnz continue_1 push 0 push offset title_box push offset ms2; пустой трэк push 0 call MessageBoxA jmp ext continue_1:

;-- недостаточно найти первый единичный символ и проверить наличие сразу после него (включая его самого) SS ;-- этот единичный символ может быть ошибкой, поэтому просканируем начиная с него- найдем комбинацию SS (если она есть) ;-- разница между смещением найденного SS и единичным символом — выведем ее с уведомлением что результат может быть неверный

xor edx, offset [Start_Sentinel+1] ; откатим edx обратно mov eax, edi mov ebx, offset Start_Sentinel search_SS: mov esi, eax inc eax mov ecx, -1 mov edi, ebx repz cmpsb; сколько байт совпало показано в ecx отрицательным числом cmp cx, -6 loopnz search_SS dec eax; откорректируем потому что eax чуть убежало в цикле sub eax, edx jz continue_2; совпала локация первого бита и SS push 4 push offset title_box push offset ms1 push 0 call MessageBoxA; повод для размышления и выбора ;[ms+191] cmp eax, 1 jnz ext continue_2:

mov byte ptr [result], 'S' ; подтвердим dec esi; смотрим на сразу после SS mov ecx, 5 mov eax, offset [en-1] sub eax, esi xor edx, edx div ecx mov ecx, eax; число итераций по 5 mov edi, offset [result+1] mov ebx, offset val_0

pars: lodsd mov edx, eax lodsb sub al, 30h sub edx, 030303030h shl dl, 4 xor al, dl shl dh, 3 xor al, dh bswap edx shl dl, 1 xor al, dl shl dh, 2 xor al, dh xlat stosb loop pars push OF_READWRITE push offset flStr push offset ResultFile call OpenFile mov dword ptr [Handle_File_Result], eax push FILE_BEGIN push NULL push 0 push eax call SetFilePointer; установим указатель на начало файла push NULL push offset NBW push 60 push offset result push dword ptr [Handle_File_Result] call WriteFile ext: push Handle_File_Input call CloseHandle push Handle_File_Result call CloseHandle push 4 push offset title_box push offset ms4 push 0 call MessageBoxA; повод для размышления и выбора cmp eax, 6 jz begin exit: push 0 call ExitProcess end start И, наконец, сам результат: S5224559648685547M08081211550005850000EM…

Который подозрительно напоминает наскрайбированные на карте цифры и буквы.

Следующая дорожка уже зашифрована, но об этом уже в другой раз.

Полезные ссылки ru.wikipedia.org/wiki/Гистерезисwww.dataip.co.uk/Reference/MagneticCardBCD.phpstripesnoop.sourceforge.net/devel/layoutstd.pdfкнига Патрика Гёлля — Магнитные карты и ПК.

© Habrahabr.ru