Compare commits

15 Commits

Author SHA1 Message Date
dandri
ad5b51b7c2 Update index.html 2025-03-27 22:31:01 +00:00
dandri
07aed533a3 Update index.html 2024-08-27 16:22:12 +00:00
dandri
d307f4b77b Update index.html 2024-08-27 16:16:24 +00:00
dandri
611587075b Update smartair.js 2024-08-25 01:25:44 +00:00
dandri
5908b7217d Update smartair.js 2024-08-25 00:29:53 +00:00
dandri
de003634fa Create jekyll-gh-pagess.yml 2024-08-24 23:34:10 +00:00
dandri
cceac1d1a9 Create jekyll-gh-pages.yml 2024-08-24 23:31:39 +00:00
dandri
490b7d66b1 Update smartair.js 2024-08-24 23:30:34 +00:00
Michael Micsen Johannessen Wehus
00924c2f52 Update smartair.js 2024-08-23 23:04:06 +02:00
Michael Micsen Johannessen Wehus
fae5181d50 Try this one 2024-08-23 19:16:27 +02:00
Michael Micsen Johannessen Wehus
5e7331f02d Add SmartAir 2023-04-08 05:27:35 +01:00
Michael Micsen Johannessen Wehus
a3c0cdd6da bug 2023-04-08 02:21:42 +01:00
Michael Micsen Johannessen Wehus
23e53f39f2 reverting 2023-04-08 02:20:31 +01:00
Michael Micsen Johannessen Wehus
48c85fc286 Add 4k support 2023-04-08 02:16:31 +01:00
Michael Micsen Johannessen Wehus
38f72a92f9 Bin to flip (#1)
Added the ability to download both BIN and NFC
2023-04-08 00:13:10 +01:00
5 changed files with 433 additions and 54 deletions

51
.github/workflows/jekyll-gh-pages.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

51
.github/workflows/jekyll-gh-pagess.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -1,20 +1,60 @@
<!DOCTYPE html>
3<!DOCTYPE html>
<html>
<body>
<title>Flipper <-> Mifare classic Binary coverter</title>
<h2>My First JavaScript</h2>
<h2>Mifare classic converter</h2>
<p>I take absolutely no liabilty for use please verify the files yourself before writing to a card you can end up
locking it</p>
bricking your cards.</p>
<script src="./script.js"></script>
<script src="./scripts/smartair.js"></script>
<script>
const blocks = 64 // Number of blocks to rea
let cardSize = 1024 //1k card
let blocks = 64 // 1k card
let filename = Date.now()
function updateCardSize(size) {
cardSize = size
blocks = size / 16
}
document.addEventListener("DOMContentLoaded", function (event) {
const nfc = document.getElementById("nfc");
document.getElementById("file-selector").addEventListener("change", (event) => {
console.log(event.target.files[0])
if (event.target.files[0].name.endsWith(".bin")) {
getBinaryFromFile(event.target.files[0]).then((data) => {
filename = event.target.files[0].name.replace(".bin", "")
const hexStr = generateNfcFromBinary(data);
nfc.value = hexStr;
updateCardSize(data.length)
})
} else if (event.target.files[0].name.endsWith(".nfc")) {
getTextFromFile(event.target.files[0]).then((data) => {
filename = event.target.files[0].name.replace(".nfc", "")
nfc.value = data;
const re = /Mifare Classic type: (1K|4K)/
const dec = re.exec(data)
console.log(dec)
if (dec) {
if (dec[1] == "1K") {
updateCardSize(1024)
} else if (dec[1] == "4K") {
updateCardSize(4096)
}
}
})
} else {
alert("Invalid file type");
return
}
});
});
function download(filename) {
var element = document.createElement('a');
function bin() {
let array = new Array(blocks)
const nfc = document.getElementById("nfc");
const nfc = document.getElementById("nfc");
var text = nfc.value.split("\n");
@@ -33,6 +73,7 @@
}
}
let textHex = ""
console.log("number of blocks", blocks)
for (let i = 0; i < blocks; i++) {
console.log('block', i, array[i])
if (array[i] !== undefined && array[i]?.length == 32) {
@@ -44,59 +85,51 @@
textHex += '00000000000000000000000000000000'
}
}
var filename = Date.now() + ".bin"
console.log(textHex)
var bin = new Array();
for (var i = 0; i < textHex.length / 2; i++) {
var h = textHex.substr(i * 2, 2);
bin[i] = parseInt(h, 16);
}
var byteArray = new Uint8Array(bin);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], { type: 'application/octet-stream' }));
a.download = filename;
// Append anchor to body.
document.body.appendChild(a)
a.click();
// Remove anchor from body
document.body.removeChild(a)
downloadBinFile(textHex, filename + ".bin")
}
document.addEventListener("DOMContentLoaded", function (event) {
const nfc = document.getElementById("nfc");
function nfc() {
downloadTextFile(document.getElementById("nfc").value, filename + ".nfc")
}
function smartAir() {
const nfc = document.getElementById("nfc");
// Allow for "Upload"
const fileSelector = document.getElementById('file-selector');
fileSelector.addEventListener('change', (event) => {
const fileList = event.target.files;
console.log(fileList);
const reader = new FileReader();
reader.addEventListener('load', (event) => {
console.log(event.target.result)
nfc.value = event.target.result;
});
reader.readAsText(event.target.files[0]);
});
})
const startSector = Number(document.getElementById("startSector").value)
const keyA = document.getElementById("keyA").value
const data = generateSmartAirCard(startSector, keyA)
console.log(data)
const hexStr = generateNfcFromBinary(data);
nfc.value = hexStr;
}
</script>
<button type="button" onclick="download('data.bin');">
Click me to download your file</button>
<p>Paste your .nfc file content here</p>
<br />
<input type="file" id="file-selector" accept=".nfc">
<br />
<textarea id="nfc" rows="20" cols="50"></textarea>
<button type="button" onclick="bin()">
Download .bin
</button>
<button type="button" onclick="nfc()">
Download .nfc
</button>
<p>.nfc file content here</p>
<br />
<input type="file" id="file-selector" accept=".bin,.nfc">
<br />
<textarea id="nfc" rows="20" cols="90"></textarea>
<br />
<p>Custom generation</p>
<p>What is this?</p>
<p>This is when someone fucked up big time and let us generate good credentials with little to no knowledge
All props goes to Miana and Micsen for their incredible research on Smartair. https://smartair.wtf</p>
<div>
<p>Key A</p><input type="text" id="keyA" />
<p>StartSector</p><input type="text" id="startSector" />
<button type="button" onclick="smartAir()">
Make smartAirDump
</button>
</div>
</body>
</html>
</html>

121
script.js Normal file
View File

@@ -0,0 +1,121 @@
function downloadBinFile(dataAsHexStr, fileName) {
var bin = new Array();
for (var i = 0; i < dataAsHexStr.length / 2; i++) {
var h = dataAsHexStr.substr(i * 2, 2);
bin[i] = parseInt(h, 16);
}
console.log(dataAsHexStr)
var byteArray = new Uint8Array(bin);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob([byteArray], { type: 'application/octet-stream' }));
a.download = fileName;
// Append anchor to body.
document.body.appendChild(a)
a.click();
// Remove anchor from body
document.body.removeChild(a)
}
function downloadTextFile(text, filename) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
function generateNfcFromBinary(data, uidLength = 4) {
console.log(data)
const dataLen = data.length;
const uid = data.slice(0, uidLength);
if (dataLen != 1024 && dataLen != 4096) {
alert("Invalid data length");
return;
}
const classicType = (dataLen == 1024) ? "1K" : "4K";
const header =
`#Generated at https://micsen.github.io/flipperNfcToBin/
Filetype: Flipper NFC device
Version: 3
# Nfc device type can be UID, Mifare Ultralight, Mifare Classic
Device type: Mifare Classic
# UID, ATQA and SAK are common for all formats
UID: ${printHex(uid)}
ATQA: ${(uidLength == 4) ? "00 04" : "00 44"}
SAK: 08
# Mifare Classic specific data
Mifare Classic type: ${classicType}
Data format version: 2
# Mifare Classic blocks, '??' means unknown data
`
let dataStr = "";
for (let i = 0; i < data.length; i += 16) {
const block = i / 16
let bytes = printHex(data.slice(i, i + 16))
dataStr += "Block " + block + ": " + bytes + "\n"
}
return header + dataStr;
}
function getBinaryFromFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(e.target.result);
resolve(data);
};
reader.onerror = (e) => {
reject(e);
};
reader.readAsArrayBuffer(file);
});
}
function getTextFromFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const data = e.target.result
resolve(data);
};
reader.onerror = (e) => {
reject(e);
};
reader.readAsText(file);
});
}
function printHex(data, lsbFirst = false) {
var hex = "";
for (var i = 0; i < data.length; i++) {
j = (lsbFirst) ? data.length - i - 1 : i;
hex += data[j].toString(16).padStart(2, '0') + " ";
}
return hex.trim();
}
function isBigSector(sector) {
return sector < 32;
}
function getBlocks(sector) {
if (sector < 32) {
const s = sector * 4
return {start: s, end: s + 3};
} else {
const s = 128 + (sector - 16) * 16
return {start: s, end: s + 15};
}
}

123
scripts/smartair.js Normal file
View File

@@ -0,0 +1,123 @@
function generateDoorSector(sector, keyA, startId) {
if (keyA.length != 12) {
//alert("KeyA must be 12 characters long")
throw "KeyA must be 12 characters long"
}
const keyB = "e7316853e731"
blocks = getBlocks(sector)
const trailer = `${keyA}7f078800${keyB}`
let id=startId
let data = ""
for (let i = blocks.start; i < blocks.end; i++) {
let block = ""
for (let j = 0; j < 4; j++) {
const door = `${id.toString(16).padStart(4, '0')}0f01`
block += door
id++
}
//console.log(block)
data += block
}
return {data: data+trailer, endDoorId: id}
}
function generateStartSector(keyA, uocBlock) {
if (keyA.length != 12) {
//alert("KeyA must be 12 characters long")
throw "KeyA must be 12 characters long"
}
const keyB = "e7316853e731"
const trailer = `${keyA}7f078800${keyB}`
const uocBlockStr = `${uocBlock.toString(16).padStart(4, '0')}`
let data = ""
const systemID = keyA.substring(4, 12)
data += `181e${systemID}00FAF8FF2ABC06A5002A`
data += `BC983E4C982345AA850${uocBlockStr}000000000`
data += "00000000000000000000000000000000"
data += trailer
//console.log(data)
return data
}
function generateUocSector(keyA, startSector) {
if (keyA.length != 12) {
//alert("KeyA must be 12 characters long")
throw "KeyA must be 12 characters long"
}
const keyB = "e7316853e731"
const trailer = `${keyA}7f078800${keyB}`
const doorBlockStart = `${getBlocks(startSector).start.toString(16).padStart(2, '0')}`
let data = ""
const systemID = keyA.substring(4, 12)
//280{log_block_start:04x}0001E00000{door_block_start:02x}0{num_doors:04x}00000000
const block0 = `28000100001E00000${doorBlockStart}0034800000000`
data += block0
data += block0
data += "00000000000000002DBD06A600000000"
data += trailer
//console.log(data)
return data
}
function fillSector(sector) {
const key = "ffffffffffff"
blocks = getBlocks(sector)
const trailer = `${key}7f078800${key}`
let data = ""
for (let i = blocks.start; i < blocks.end; i++) {
data+="00000000000000000000000000000000"
}
data+=trailer
return data
}
function isBigSector(sector) {
return sector > 31;
}
function getBlocks(sector) {
if (sector < 32) {
const s = sector * 4
return {start: s, end: s + 3};
} else {
const s = 128 + (sector - 16) * 16
return {start: s, end: s + 15};
}
}
function generateSmartAirCard(startSector, keyA) {
let door = 0
let doorSectorFrom = startSector+2
let uocBlock = getBlocks(startSector+1).start
let dat = ""
for (let i = 0; i < 40; i++) {
if (i == 0) {
//Header block...
dat += "968B54145D880400C844002000000018"
dat += "00000000000000000000000000000000"
dat += "00000000000000000000000000000000"
dat += "FFFFFFFFFFFFFF078069FFFFFFFFFFFF"
} else if (i == startSector) {
dat += generateStartSector(keyA, uocBlock)
} else if (i == startSector+1) {
dat += generateUocSector(keyA, doorSectorFrom)
} else if (i >= doorSectorFrom) {
const {data, endDoorId} = generateDoorSector(i, keyA, door)
dat += data
door = endDoorId+1
} else {
dat += fillSector(i)
}
}
var bin = new Array();
for (var i = 0; i < dat.length / 2; i++) {
var h = dat.substr(i * 2, 2);
bin[i] = parseInt(h, 16);
}
return new Uint8Array(bin)
}