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

в 11:21, , рубрики: Карты АТМ, Промышленное программирование, реверс-инжиниринг, метки:

Предмет интереса этой публикации — считывание и декодирование данных со второй дорожки банкоматовской карточки в условиях дефицита оборудования и средств.

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

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

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

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

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

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

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

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

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

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

— информацию нужно записывать участками фиксированной длины;
— записывать на полоску можно 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. Выходов из сложившейся ситуации масса, от линии синхронизации до частотного кодирования. Которое и является основным в магнитных картах — это удвоение частоты F2F.

(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 ориентирами.

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

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

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

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

ISO 7810 Physical Characteristics of Credit.Card Size Document
ISO 7811-1 Embossing
ISO 7811-2 Magnetic Stripe — Low Coercivity
ISO 7811-3 Location of Embossed Characters
ISO 7811-4 Location of Tracks 1 and 2
ISO 7811-5 Location of Track 3
ISO 7811-6 Magnetic Stripe — High Coercivity
ISO 7813 Financial Transaction Cards

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

TRACK 1 IATA
TRACK 2 ABA
TRACK 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 masm32includewindows.inc
    include masm32includemasm32.inc
    include masm32includegdi32.inc
    include masm32includeuser32.inc
    include masm32includekernel32.inc
    include masm32includeshell32.inc
    includelib masm32libmasm32.lib
    includelib masm32libgdi32.lib
    includelib masm32libuser32.lib
    includelib masm32libkernel32.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
<habracut />
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.php
stripesnoop.sourceforge.net/devel/layoutstd.pdf
книга Патрика Гёлля – Магнитные карты и ПК.

Автор: v_joker

Источник



https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js