From d372569b45b7ce862d2ed2db192eead861806251 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 7 Sep 2017 09:33:32 +0200 Subject: [PATCH 01/19] FPGA changes ISO14443B: * slightly increase reader field strength * increase sensitivity when reading, allowing increased reading distance --- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_rx_xcorr.v | 31 +++++++++++++++++-------------- fpga/hi_read_tx.v | 16 +++++++++------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index b9f2e6292e42801a73b57aa3e43dff00191ec8b9..bd8cf8e7dcf7ae80756e29f2417a94da7e9dd290 100644 GIT binary patch literal 42175 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�{n79a~&>|uqF{t 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

N^drwI3p?g=l3-PrH` z!NC&zf?ORkdcc75F9FAUAQzsvpZLxt_|?NaMZM&J_~ia^z4H@hK*!7R3)cd42vCol z)4gFrbc$C6^xZ7wUw|!Xjb52>w$L3pYoz-N_P51uw_&KVgnt3H3UZ~mVVUx%;ZpnpJz3lb{&i8HJTHg)%uy)CKvsfZ_%i{#Ud+EB z&J4nIXpjawtruSXVJo|Zz#W29kwp9u=Sm(#{>!i5;QB+xTq}7;1m=Q`UW~9P%@Pi9 zv3`RgTUi6uA==7)`0VyJ7^8510l&EZ5OVe4&u26f%%BT^=6f?9PDY346zexM3NIcH zqo~Hh$w<_tsEc&{9spShe&Icb@$1dtJ1dSPvV%y3#X4_8{_B+@e&N1l1)uh#sX+&1 ze^gYf!JgI}3RKGYSAvlL3L`!r&tc5C-OEi_VIj_W{$VbJ5kkQ44P;nYujS)_tqOW7*)fSij+X_SAJiXe{TjoQF+wf+WOSba&#Ja_ z3_eL2e(5Z^!}Bwa{Gh|RTDG)){)_7m;jc=>MdyKbJva^}nujL|g*&g>JUhnqhmR=O zmL61EZdZ+`8<78U9_epMHLSxbJ3Jnbf9cq1D-B9sQ_rCqZ&+EH|3XYg#gTo(J(F!J zkqIvcqIC^Na>e`?^DlT!Zum{`uMUE_;3t>!FRmiMp-DEUpaai3M?)w;-BpfX&!j%6k>yA__bJC%)d5yru*kYleK*!sNZ1xTHI^BqVr!G0`RxAa4GJ# zx`9&s()JnCI}u7c37T_q80YLpkpBwmH-KLu*!f+?GxX+2=U1ZN8a#tbnYv_jvcSK% z{*dE`5l$g3%tL_Q8@nv{fbd`C>9Sh5##(9n2XQ9aQ`93%(BOb zzK{G@_>%+ra72oettI?R|2dc#2T_|=-JGxNpdXleHp=){XDZJ8>ySOV3WRro1GMT( zJW!~gNBtp22v0bT`%-k;3-QA=DiYU6KXhxceuGy~FQu9YwVMgo;Kl8VM2}p8UkYq1 zZz0~21#G>h9_?+-F#q!6^ZM_9!9@q-m=O4z9Y>&FY)KN6;gb9pcCTm|5C5h3i5g;} zD#fIYh$3w($$x47bv}C6-~}2}NMNmlYxGY4N+Wu2H4}ve19+VulSv~AK(BjcHf8Yz-l3@SB$5I==Q@mOOO$*Un#^75!=au@648bKE(pnw+4yqo0k^vkK&ri_P4C_ zMhm^0hC$01ugP&5&BOQe`B$3x7d>$#y6>Yb4)hD_B@o4mm*&5~WnlRe`E4Ds32>PR z4j<8Z7g{+z=yMQdZuBTsRxD2KVB(yIdw>JKe8ZKmUjhHh!W1wy!OL2LAja1(rRxt@x{}9saul>!zu}EJT9*Ghqb*4} zesxV?nY81Pi@tpS1q%z8i~brKr}4|LKb%+NANxw7e*U!jm4cmr2a1+tTj$3Q6-=Do zehRQP;jk3gnK4hl3h~3LLRm|g@4NU%!u({5=f4=gy1Z+9*;zs;hM%xp#rroN=e6Ma zLoFZ_&1*2HF2k=53H;K(J~n$NCd$94#4g1z_!9njRQRBG5xon(+(we+;`|q+pn_bv z>iJj{t`UMzqstMP|KGJ|B;w)YW(_`D9L~Q8v34Cf{+F357lh@{)F%kg86^b z)c^Y_Lj}29z_GuAX9g#)Nbz{|{ZW_=U#^aIk#s?>$HPJW2I~&8$sD^tW6f}{@m2a~ zJb?u$$$vef5;=d&i%+7+kng-|$(7~5AbaanLH3c6rLm3RUo3lFS^ldloyZs*KkP_H zP9tE88*dbhLwWv-<53#62t}cOqon>2SOA|N;^;;$Vh{R(#FF|$=3np+z4|(Y4LN@J zDzM;E{AyiO=+hHVqmRZfa1d`zEtByp;a}T}`a{dE_WzJ&$^9ECw$F}L7;|J~jwAH_ zepVTN@v&up%o1gy2qf~REjjhn zFa;ivA?;;?e{Vm|Ai4I{4t|=a!xZG28=mfz%Py;`d4#! zi=yv8JbQWm3;Q`zn4hr1-%ru=Qh^^Ith!JKtIH>;URFO77o)g{00u zhK2M^iZV{uYui%He<6P8@vnkC95>+Dr(q9gfGm2RJ^X@K;OiF;i_+elL>#!sp!{?4)8D7!@S{t7w5kq z#Su>~(V2sy`2r@PX!N`4=$FPX8E3iJTx@GV0LQnPW&8_x>dU2|M818_m&Fg6ZSk0Y zf`rkINpJA{#OGi1^eUrIp0KYoYYqpEioR9)iMxci7}3*fD((z@R({5=AFH^tqR^nv z%5J;fUDB)jC=qg0kAuA9z-0~P=q|wGP>R;k6;z@8M#yXY9-jDpp6;pm%g|@lXPo-+ z%Nm96dRkJ7O8&64{}-k$;znW2J!ULq;YL&&M?<}aGM!KZKMDHWRMFkvo4GAsUtMU3 z3fbSAy)98s1-T-jTj^77eX?S$*XTpzGbG*Gq$vZrNFsb?Qiuu-54P~%s~SDopH2bF z1`y1HljSvf(5To-jmlc(f7jEpA{F{0%+EDSH$E-v)vWcQ9{M!Z2V<5-3Mc=TQH}B& z6~m#F;XNtH-fbrPBhG5eKB60|bVF_DCxtmwgpCb0NaPk9ZKn6cA^n3jO{%r1m^6IW-QhkN*mD#+DcDz`rYNpB_sxw_3%e}i)) zisuTA0dr}8m2+cHZO|uGu`cv!`jo2o8h?mJsDZla230Q$>s1+ArFv6y8!E~gxA7D5 z*8*~pl8%kA2savS+#3}#)v89j$E1Q>HyVGaUP)csP~$bOYlz%v)bldti_X06~60+Zc3>Jr_u@y=!V{~DXbTssKS^9 z;L~!tEqg#O?i#ChV6KA|#FL;;H*1q~n^mvXD*35MeRoK@w>fjI3M$BDgnHfElJ${_ z)y2jq)oo6_Rl!Q7e=O=Y?S7~)8hEybYFWP;_=ztT-Ow#aDR47Oa3kZyxzVqm6ut|x zr$XzXP1ks4ht`j!gu+91l;le8hE|m7@X@oUFbs9A)#c(j+@*dG>T7xJ`v+q zdd0mqS+f?6G1o~(*3P*#q@Tz_C#&KwiLs!fhy}BJbZ`rOni_hZv6l>Ozuj*{Zf~-s zTbCT#-dN~DT^Db;_)izlTzv2HM$qTq(n$O$yzHshv->x4AD#kX?NHFiGXF9(PyN1AJAPRs_^#jj z9^i7}yQ~5n2e|AvHlUu>|5;?-)L)lbGg5nb!|!3a5ty#IZbcHN>(Ykw&4wGmF&ZHc zpb_>#Lx!N8{A0PYpf;>O<%(47V=~U%NVV$nhW8#jBt?pLLF=m2WsL!TF@|u%CAb^_ zG~=>o4<-6tj?2Lleho>C6bVQNk=!GfHC)|Af0vJ^14TUbpD6#kLfzVQU+S1>fL7jn zdPCip{d(|1zmL2@n!Ta9bOY61-stwetA{FSm9kT2L+J8`_f^zG8jb^8E^GLGm}X?mjnIM8&N{`+i?=OvLST$Wew$xhi*ujcOee)`>Hl~S);HwuwG@j8~`-qviBZ| z^(x@_gelIez$Bmo}&%mqEQiw~9z;^<@p|e-{u2aLjJ?HQ{TC6#aa{qI z%NnZ+UH(7C<#ISK0Q7$jzUy*0EgM`0BW)~Xsj%-doIDseg z+@Mn_6=Z{5LJosIxZ;b||M@rkUq{F9=oF3qjt~F;`l##T#fvV#{PITdm6v}M zJo%rtPeFwPT`o3)F<+*Gzgw3t-K+n7?_uip{yONt=eKEK0bjV=|2tjAn+p0x1r?t6 z9F%&ZOsRuDzeAUUKJTxC+WHmxyq_)?l=}ZcmrLJ6<%X#+EuQWc*W&$j*@tSs({Isb z@6!)-`Tb16|5$x5F79t@CCLf#c1u$C$A4R&51xN810T%52Q%=&416#HAI!i9Gw|fbUG We*cOjW%&2Fq5%IME~cgYm;B#7qqOM& diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index afa46c4..433d673 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -34,13 +34,13 @@ always @(negedge ck_1356megb) (* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) - if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz + if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz, standard ISO14443B adc_clk <= ck_1356megb; - else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 424.25 kHz + else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 423.75 kHz adc_clk <= fc_div[0]; - else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 212.125 kHz + else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 211.875 kHz adc_clk <= fc_div[1]; - else // fc = 106.0625 kHz + else // fc = 105.9375 kHz adc_clk <= fc_div[2]; // When we're a reader, we just need to do the BPSK demod; but when we're an @@ -69,13 +69,16 @@ begin end end -// Let us report a correlation every 4 subcarrier cycles, or 4*16 samples, +// Let us report a correlation every 4 subcarrier cycles, or 4*16=64 samples, // so we need a 6-bit counter. reg [5:0] corr_i_cnt; // And a couple of registers in which to accumulate the correlations. -// we would add/sub at most 32 times adc_d, the signed result can be held in 14 bits. -reg signed [13:0] corr_i_accum; -reg signed [13:0] corr_q_accum; +// We would add at most 32 times the difference between unmodulated and modulated signal. It should +// be safe to assume that a tag will not be able to modulate the carrier signal by more than 25%. +// 32 * 255 * 0,25 = 2040, which can be held in 11 bits. Add 1 bit for sign. +reg signed [11:0] corr_i_accum; +reg signed [11:0] corr_q_accum; +// we will report maximum 8 significant bits reg signed [7:0] corr_i_out; reg signed [7:0] corr_q_out; // clock and frame signal for communication to ARM @@ -99,16 +102,16 @@ begin begin if(snoop) begin - // Send only 7 most significant bits of tag signal (signed), LSB is reader signal: - corr_i_out <= {corr_i_accum[13:7], after_hysteresis_prev_prev}; - corr_q_out <= {corr_q_accum[13:7], after_hysteresis_prev}; + // Send 7 most significant bits of tag signal (signed), plus 1 bit reader signal + corr_i_out <= {corr_i_accum[11:5], after_hysteresis_prev_prev}; + corr_q_out <= {corr_q_accum[11:5], after_hysteresis_prev}; after_hysteresis_prev_prev <= after_hysteresis; end else begin - // 8 most significant bits of tag signal - corr_i_out <= corr_i_accum[13:6]; - corr_q_out <= corr_q_accum[13:6]; + // 8 bits of tag signal + corr_i_out <= corr_i_accum[11:4]; + corr_q_out <= corr_q_accum[11:4]; end corr_i_accum <= adc_d; diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index f12e64e..fc309cd 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -24,33 +24,36 @@ module hi_read_tx( output dbg; input shallow_modulation; +// low frequency outputs, not relevant +assign pwr_lo = 1'b0; +assign pwr_oe2 = 1'b0; + // The high-frequency stuff. For now, for testing, just bring out the carrier, // and allow the ARM to modulate it over the SSP. reg pwr_hi; reg pwr_oe1; -reg pwr_oe2; reg pwr_oe3; reg pwr_oe4; + always @(ck_1356megb or ssp_dout or shallow_modulation) begin if(shallow_modulation) begin pwr_hi <= ck_1356megb; - pwr_oe1 <= ~ssp_dout; - pwr_oe2 <= ~ssp_dout; - pwr_oe3 <= ~ssp_dout; - pwr_oe4 <= 1'b0; + pwr_oe1 <= 1'b0; + pwr_oe3 <= 1'b0; + pwr_oe4 <= ~ssp_dout; end else begin pwr_hi <= ck_1356megb & ssp_dout; pwr_oe1 <= 1'b0; - pwr_oe2 <= 1'b0; pwr_oe3 <= 1'b0; pwr_oe4 <= 1'b0; end end + // Then just divide the 13.56 MHz clock down to produce appropriate clocks // for the synchronous serial port. @@ -83,7 +86,6 @@ end assign ssp_din = after_hysteresis; -assign pwr_lo = 1'b0; assign dbg = ssp_din; endmodule From da36bed90d815171a54456c693f2fea8c12e66b3 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 22 Oct 2017 21:47:19 +0300 Subject: [PATCH 02/19] added testing timeout 40 sec to avoid hang VM --- appveyor.yml | 74 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a4a70f7..daf883f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -63,80 +63,10 @@ install: build_script: - ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n\n#make\nbash -lc -i \"pwd;make all\"\n\n#some checks\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\proxmark3.exe)){\nthrow \"Main file proxmark3.exe not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\armsrc\\obj\\fullimage.elf)){\nthrow \"ARM file fullimage.elf not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z)){\nthrow \"Files in hardnested\\tables not exists.\"\n}\n\n#copy\nWrite-Host \"Copy release files...\" -NoNewLine -ForegroundColor Yellow\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\nCopy-Item C:\\ProxSpace\\pm3\\client\\*.exe C:\\ProxSpace\\Release\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\armsrc\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\bootrom\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\scripts\nCopy-Item C:\\ProxSpace\\pm3\\client\\scripts\\*.lua C:\\ProxSpace\\Release\\scripts\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\hardnested\\tables\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\*.bin C:\\ProxSpace\\Release\\hardnested\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z C:\\ProxSpace\\Release\\hardnested\\tables\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\n#archive and push\n$releasename=\"\"\nif ($env:appveyor_repo_tag -match \"true\"){\n$releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + \"]\" \n\nWrite-Host \"Archive and publish release files ($releasename)...\" -NoNewLine -ForegroundColor Yellow\ncd C:\\ProxSpace\n7z a release.zip C:\\ProxSpace\\Release\nPush-AppveyorArtifact release.zip -DeploymentName \"$releasename\"\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\nWrite-Host \"Builded...\" -ForegroundColor Yellow" test_script: -- ps: >- - $env:Path = "C:\ProxSpace\msys\bin;$env:Path" - - cd c:\ProxSpace\pm3 - - - $global:TestsPassed=$true - - $global:TestTime=[System.Environment]::TickCount - - - Function ExecTest($Name, $File, $Cond) { - [bool]$res=$false; - if ($Cond -eq $null){ - } Else { - If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ - if ($Cond -is "String" -and $Cond -like "*passed*"){ - $res= $true - } - if ($Cond -is "String" -and $Cond -like "*true*"){ - $res= $true - } - } Else { - $res=$Cond; - } - } - - If ($res) { - Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$global:TestTime)" - }Else { - Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$global:TestTime)" - $global:TestsPassed=$false - } - $global:TestTime=[System.Environment]::TickCount - } - - - Write-Host "Running tests..." -ForegroundColor Yellow - - - #file test - - ExecTest "proxmark3 exists" "proxmark3.exe" $(Test-Path C:\ProxSpace\Release\proxmark3.exe) - - ExecTest "arm image exists" "\arm\fullimage1.elf" $(Test-Path C:\ProxSpace\Release\arm\fullimage.elf) - - ExecTest "bootrom exists" "bootrom.elf" $(Test-Path C:\ProxSpace\Release\arm\bootrom.elf) - - ExecTest "hardnested tables exists" "hardnested" $(Test-Path C:\ProxSpace\Release\hardnested\tables\*.z) - - ExecTest "release exists" "release.zip" $(Test-Path C:\ProxSpace\release.zip) - - - #proxmark logic tests - - ExecTest "proxmark help" "proxmark3 -h" $(bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed') - - ExecTest "proxmark help hardnested" "proxmark3 -h" $(bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed') - - - ExecTest "hf mf offline text" "hf mf" $(bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed") - - ExecTest "hf mf hardnested" "hf mf hardnested" $(bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed") - - - if ($global:TestsPassed) { - Write-Host "Tests [ OK ]" -ForegroundColor Green - } else { - Write-Host "Tests [ ERROR ]" -ForegroundColor Red - throw "Tests error." - } +- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\ncd c:\\ProxSpace\\pm3\n\n$global:TestsPassed=$true\n\nFunction ExecTest($Name, $File, $Cmd) {\n#--- begin Job \n$Job = Start-Job -ScriptBlock { \n [bool]$res=$false;\n $TestTime=[System.Environment]::TickCount\n $env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n Set-Location $using:PWD\n\n $sb=[scriptblock]::Create(\"$using:Cmd\")\n #execute scriptblock\n $Cond=&$sb\n\n if ($Cond -eq $null){\n } Else {\n If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){\n if ($Cond -is \"String\" -and $Cond -like \"*passed*\"){\n $res= $true\n }\n if ($Cond -is \"String\" -and $Cond -like \"*true*\"){\n $res= $true\n }\n } Else {\n $res=$Cond;\n }\n }\n\n If ($res) {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Passed -Duration \"$([System.Environment]::TickCount-$TestTime)\"\n }Else {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Failed -Duration \"$([System.Environment]::TickCount-$TestTime)\" -ErrorMessage \"command:$using:Cmd`nresult:$Cond\"\n }\n return $res\n}\n#--- end Job\n\n [bool]$res=$false\n # Wait 40 sec timeout for Job\n if(Wait-Job $Job -Timeout 40){\n $Results = $Job | Receive-Job \n if($Results -like \"true\"){\n $res=$true\n }\n } else {\n Add-AppveyorTest -Name \"$Name\" -Framework NUnit -Filename \"$File\" -Outcome Failed -Duration 40000 -ErrorMessage \"timeout\"\n }\n Remove-Job -Force $Job\n\n if(!$res){\n $global:TestsPassed=$false\n }\n}\n\nWrite-Host \"Running tests...\" -ForegroundColor Yellow\n\n#file test\nExecTest \"proxmark3 exists\" \"proxmark3.exe\" {Test-Path C:\\ProxSpace\\Release\\proxmark3.exe}\nExecTest \"arm image exists\" \"\\arm\\fullimage1.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\fullimage.elf}\nExecTest \"bootrom exists\" \"bootrom.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\bootrom.elf}\nExecTest \"hardnested tables exists\" \"hardnested\" {Test-Path C:\\ProxSpace\\Release\\hardnested\\tables\\*.z}\nExecTest \"release exists\" \"release.zip\" {Test-Path C:\\ProxSpace\\release.zip}\n\n#proxmark logic tests\nExecTest \"proxmark help\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'}\nExecTest \"proxmark help hardnested\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'}\n\nExecTest \"hf mf offline text\" \"hf mf\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed\"}\nExecTest \"hf mf hardnested\" \"hf mf hardnested\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed\"}\n\nif ($global:TestsPassed) {\n Write-Host \"Tests [ OK ]\" -ForegroundColor Green\n} else {\n Write-Host \"Tests [ ERROR ]\" -ForegroundColor Red\n throw \"Tests error.\"\n}" on_success: - ps: Write-Host "Build success..." -ForegroundColor Green on_failure: - ps: Write-Host "Build error." -ForegroundColor Red on_finish: -- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) \ No newline at end of file +- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) From 9764d3ea0ec65f520efd4528301da00eedbfbad4 Mon Sep 17 00:00:00 2001 From: merlokk Date: Sun, 22 Oct 2017 22:20:44 +0300 Subject: [PATCH 03/19] normally show powershell scripts it needs to have no spaces at the end of lines and no `;` --- appveyor.yml | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 204 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index daf883f..58bb136 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,29 @@ version: 3.0.1.{build} image: Visual Studio 2017 clone_folder: C:\ProxSpace\pm3 init: -- ps: "$psversiontable\n#Get-ChildItem Env:\n\n$releasename=\"\"\n$env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8)\nif ($env:appveyor_repo_tag -match \"true\"){\n $releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT_SHORT + \"]\" \n\nWrite-Host \"repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename\" -ForegroundColor Yellow\nAdd-AppveyorMessage -Message \"[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)\" -Category Information -Details \"repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename\"\n\niex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))" +- ps: >- + $psversiontable + + #Get-ChildItem Env: + + + $releasename="" + + $env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8) + + if ($env:appveyor_repo_tag -match "true"){ + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT_SHORT + "]" + + + Write-Host "repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename" -ForegroundColor Yellow + + Add-AppveyorMessage -Message "[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)" -Category Information -Details "repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename" + + + iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) clone_script: - ps: >- Write-Host "Removing ProxSpace..." -NoNewLine @@ -61,9 +83,188 @@ install: } } build_script: -- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n\n#make\nbash -lc -i \"pwd;make all\"\n\n#some checks\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\proxmark3.exe)){\nthrow \"Main file proxmark3.exe not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\armsrc\\obj\\fullimage.elf)){\nthrow \"ARM file fullimage.elf not exists.\"\n}\nif(!(Test-Path C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z)){\nthrow \"Files in hardnested\\tables not exists.\"\n}\n\n#copy\nWrite-Host \"Copy release files...\" -NoNewLine -ForegroundColor Yellow\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\nCopy-Item C:\\ProxSpace\\pm3\\client\\*.exe C:\\ProxSpace\\Release\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\armsrc\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nCopy-Item C:\\ProxSpace\\pm3\\bootrom\\obj\\*.elf C:\\ProxSpace\\Release\\arm\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\scripts\nCopy-Item C:\\ProxSpace\\pm3\\client\\scripts\\*.lua C:\\ProxSpace\\Release\\scripts\nNew-Item -ItemType Directory -Force -Path C:\\ProxSpace\\Release\\hardnested\\tables\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\*.bin C:\\ProxSpace\\Release\\hardnested\nCopy-Item C:\\ProxSpace\\pm3\\client\\hardnested\\tables\\*.bin.z C:\\ProxSpace\\Release\\hardnested\\tables\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\n#archive and push\n$releasename=\"\"\nif ($env:appveyor_repo_tag -match \"true\"){\n$releasename=$env:APPVEYOR_REPO_TAG_NAME + \"/\"\n}\n$releasename+=$env:APPVEYOR_BUILD_VERSION + \" [\" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + \"]\" \n\nWrite-Host \"Archive and publish release files ($releasename)...\" -NoNewLine -ForegroundColor Yellow\ncd C:\\ProxSpace\n7z a release.zip C:\\ProxSpace\\Release\nPush-AppveyorArtifact release.zip -DeploymentName \"$releasename\"\nWrite-Host \"[ OK ]\" -ForegroundColor Green\n\nWrite-Host \"Builded...\" -ForegroundColor Yellow" +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + + #make + + bash -lc -i "pwd;make all" + + + #some checks + + if(!(Test-Path C:\ProxSpace\pm3\client\proxmark3.exe)){ + + throw "Main file proxmark3.exe not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\armsrc\obj\fullimage.elf)){ + + throw "ARM file fullimage.elf not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z)){ + + throw "Files in hardnested\tables not exists." + + } + + + #copy + + Write-Host "Copy release files..." -NoNewLine -ForegroundColor Yellow + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release + + Copy-Item C:\ProxSpace\pm3\client\*.exe C:\ProxSpace\Release + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\arm + + Copy-Item C:\ProxSpace\pm3\armsrc\obj\*.elf C:\ProxSpace\Release\arm + + Copy-Item C:\ProxSpace\pm3\bootrom\obj\*.elf C:\ProxSpace\Release\arm + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\scripts + + Copy-Item C:\ProxSpace\pm3\client\scripts\*.lua C:\ProxSpace\Release\scripts + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\hardnested\tables + + Copy-Item C:\ProxSpace\pm3\client\hardnested\*.bin C:\ProxSpace\Release\hardnested + + Copy-Item C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z C:\ProxSpace\Release\hardnested\tables + + Write-Host "[ OK ]" -ForegroundColor Green + + + #archive and push + + $releasename="" + + if ($env:appveyor_repo_tag -match "true"){ + + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + "]" + + + Write-Host "Archive and publish release files ($releasename)..." -NoNewLine -ForegroundColor Yellow + + cd C:\ProxSpace + + 7z a release.zip C:\ProxSpace\Release + + Push-AppveyorArtifact release.zip -DeploymentName "$releasename" + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Builded..." -ForegroundColor Yellow test_script: -- ps: "$env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\ncd c:\\ProxSpace\\pm3\n\n$global:TestsPassed=$true\n\nFunction ExecTest($Name, $File, $Cmd) {\n#--- begin Job \n$Job = Start-Job -ScriptBlock { \n [bool]$res=$false;\n $TestTime=[System.Environment]::TickCount\n $env:Path = \"C:\\ProxSpace\\msys\\bin;$env:Path\"\n Set-Location $using:PWD\n\n $sb=[scriptblock]::Create(\"$using:Cmd\")\n #execute scriptblock\n $Cond=&$sb\n\n if ($Cond -eq $null){\n } Else {\n If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){\n if ($Cond -is \"String\" -and $Cond -like \"*passed*\"){\n $res= $true\n }\n if ($Cond -is \"String\" -and $Cond -like \"*true*\"){\n $res= $true\n }\n } Else {\n $res=$Cond;\n }\n }\n\n If ($res) {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Passed -Duration \"$([System.Environment]::TickCount-$TestTime)\"\n }Else {\n Add-AppveyorTest -Name \"$using:Name\" -Framework NUnit -Filename \"$using:File\" -Outcome Failed -Duration \"$([System.Environment]::TickCount-$TestTime)\" -ErrorMessage \"command:$using:Cmd`nresult:$Cond\"\n }\n return $res\n}\n#--- end Job\n\n [bool]$res=$false\n # Wait 40 sec timeout for Job\n if(Wait-Job $Job -Timeout 40){\n $Results = $Job | Receive-Job \n if($Results -like \"true\"){\n $res=$true\n }\n } else {\n Add-AppveyorTest -Name \"$Name\" -Framework NUnit -Filename \"$File\" -Outcome Failed -Duration 40000 -ErrorMessage \"timeout\"\n }\n Remove-Job -Force $Job\n\n if(!$res){\n $global:TestsPassed=$false\n }\n}\n\nWrite-Host \"Running tests...\" -ForegroundColor Yellow\n\n#file test\nExecTest \"proxmark3 exists\" \"proxmark3.exe\" {Test-Path C:\\ProxSpace\\Release\\proxmark3.exe}\nExecTest \"arm image exists\" \"\\arm\\fullimage1.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\fullimage.elf}\nExecTest \"bootrom exists\" \"bootrom.elf\" {Test-Path C:\\ProxSpace\\Release\\arm\\bootrom.elf}\nExecTest \"hardnested tables exists\" \"hardnested\" {Test-Path C:\\ProxSpace\\Release\\hardnested\\tables\\*.z}\nExecTest \"release exists\" \"release.zip\" {Test-Path C:\\ProxSpace\\release.zip}\n\n#proxmark logic tests\nExecTest \"proxmark help\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'}\nExecTest \"proxmark help hardnested\" \"proxmark3 -h\" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'}\n\nExecTest \"hf mf offline text\" \"hf mf\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed\"}\nExecTest \"hf mf hardnested\" \"hf mf hardnested\" {bash -lc \"cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed\"}\n\nif ($global:TestsPassed) {\n Write-Host \"Tests [ OK ]\" -ForegroundColor Green\n} else {\n Write-Host \"Tests [ ERROR ]\" -ForegroundColor Red\n throw \"Tests error.\"\n}" +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + cd c:\ProxSpace\pm3 + + + $global:TestsPassed=$true + + + Function ExecTest($Name, $File, $Cmd) { + + #--- begin Job + + $Job = Start-Job -ScriptBlock { + [bool]$res=$false + $TestTime=[System.Environment]::TickCount + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + Set-Location $using:PWD + + $sb=[scriptblock]::Create("$using:Cmd") + #execute scriptblock + $Cond=&$sb + + if ($Cond -eq $null){ + } Else { + If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ + if ($Cond -is "String" -and $Cond -like "*passed*"){ + $res= $true + } + if ($Cond -is "String" -and $Cond -like "*true*"){ + $res= $true + } + } Else { + $res=$Cond + } + } + + If ($res) { + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" + }Else { + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" -ErrorMessage "command:$using:Cmd`nresult:$Cond" + } + return $res + } + + #--- end Job + + [bool]$res=$false + # Wait 40 sec timeout for Job + if(Wait-Job $Job -Timeout 40){ + $Results = $Job | Receive-Job + if($Results -like "true"){ + $res=$true + } + } else { + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 40000 -ErrorMessage "timeout" + } + Remove-Job -Force $Job + + if(!$res){ + $global:TestsPassed=$false + } + } + + + Write-Host "Running tests..." -ForegroundColor Yellow + + + #file test + + ExecTest "proxmark3 exists" "proxmark3.exe" {Test-Path C:\ProxSpace\Release\proxmark3.exe} + + ExecTest "arm image exists" "\arm\fullimage1.elf" {Test-Path C:\ProxSpace\Release\arm\fullimage.elf} + + ExecTest "bootrom exists" "bootrom.elf" {Test-Path C:\ProxSpace\Release\arm\bootrom.elf} + + ExecTest "hardnested tables exists" "hardnested" {Test-Path C:\ProxSpace\Release\hardnested\tables\*.z} + + ExecTest "release exists" "release.zip" {Test-Path C:\ProxSpace\release.zip} + + + #proxmark logic tests + + ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q Execute && echo Passed || echo Failed'} + + ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} + + + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed"} + + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed"} + + + if ($global:TestsPassed) { + Write-Host "Tests [ OK ]" -ForegroundColor Green + } else { + Write-Host "Tests [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } on_success: - ps: Write-Host "Build success..." -ForegroundColor Green on_failure: From 05b6b117850e7d60a0fb0609550f3c029d98201c Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Mon, 23 Oct 2017 18:04:06 +1100 Subject: [PATCH 04/19] Multiple USB-CDC changes (attempt 6) (#439) - Fix reporting of string descriptors. - Add note about ModemManager matching on the manufacturer string. --- common/usb_cdc.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/common/usb_cdc.c b/common/usb_cdc.c index 56690ad..91a63cf 100644 --- a/common/usb_cdc.c +++ b/common/usb_cdc.c @@ -44,6 +44,11 @@ #define AT91C_EP_OUT_SIZE 0x40 #define AT91C_EP_IN_SIZE 0x40 +// Language must always be 0. +#define STR_LANGUAGE_CODES 0x00 +#define STR_MANUFACTURER 0x01 +#define STR_PRODUCT 0x02 + static const char devDescriptor[] = { /* Device descriptor */ 0x12, // bLength @@ -56,8 +61,8 @@ static const char devDescriptor[] = { 0xc4,0x9a, // Vendor ID (0x9ac4 = J. Westhues) 0x8f,0x4b, // Product ID (0x4b8f = Proxmark-3 RFID Instrument) 0x01,0x00, // Device release number (0001) - 0x01, // iManufacturer - 0x02, // iProduct + STR_MANUFACTURER, // iManufacturer + STR_PRODUCT, // iProduct 0x00, // iSerialNumber 0x01 // bNumConfigs }; @@ -157,7 +162,9 @@ static const char StrDescLanguageCodes[] = { 0x03, // Type is string 0x09, 0x04 // supported language Code 0 = 0x0409 (English) }; - + +// Note: ModemManager (Linux) ignores Proxmark3 devices by matching the +// manufacturer string "proxmark.org". Don't change this. static const char StrDescManufacturer[] = { 26, // Length 0x03, // Type is string @@ -182,20 +189,18 @@ static const char StrDescProduct[] = { 'M', 0x00, '3', 0x00 }; - -static const char* const pStrings[] = -{ - StrDescLanguageCodes, - StrDescManufacturer, - StrDescProduct -}; const char* getStringDescriptor(uint8_t idx) { - if(idx >= (sizeof(pStrings) / sizeof(pStrings[0]))) { - return(NULL); - } else { - return(pStrings[idx]); + switch (idx) { + case STR_LANGUAGE_CODES: + return StrDescLanguageCodes; + case STR_MANUFACTURER: + return StrDescManufacturer; + case STR_PRODUCT: + return StrDescProduct; + default: + return NULL; } } From b5381d70dcbe6599dcb472fb65a4fe0bc8de98f9 Mon Sep 17 00:00:00 2001 From: merlokk Date: Mon, 23 Oct 2017 13:43:46 +0300 Subject: [PATCH 05/19] added write-host for tests --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 58bb136..b072335 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -187,7 +187,9 @@ test_script: $sb=[scriptblock]::Create("$using:Cmd") #execute scriptblock + Write-host "Test [$using:Name] job: $using:Cmd" $Cond=&$sb + Write-host "Result[$using:Name]: $Cond" if ($Cond -eq $null){ } Else { @@ -221,6 +223,7 @@ test_script: $res=$true } } else { + Write-host "Test [$Name] timeout" -ForegroundColor Red Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 40000 -ErrorMessage "timeout" } Remove-Job -Force $Job From 23af9327a53b865383271e5844f75997671b32aa Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 24 Oct 2017 07:48:38 +0200 Subject: [PATCH 06/19] fix hw status (and USB comm in general) (#434) * don't ignore ReadLine() errors (Windows) * lock CmdBuffer with Mutex * refactor WaitForResponseTimeoutW --- client/cmdmain.c | 26 +++++++++++++++++++++----- client/proxmark3.c | 3 +-- uart/uart_win32.c | 8 +++----- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/client/cmdmain.c b/client/cmdmain.c index db88b3a..739d68e 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -8,6 +8,9 @@ // Main command parser entry point //----------------------------------------------------------------------------- +#include "cmdmain.h" + +#include #include #include #include @@ -21,7 +24,6 @@ #include "cmddata.h" #include "cmdhw.h" #include "cmdlf.h" -#include "cmdmain.h" #include "util.h" #include "util_posix.h" #include "cmdscript.h" @@ -41,6 +43,8 @@ static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; static int cmd_head;//Starts as 0 //Points to the position of the last unread command static int cmd_tail;//Starts as 0 +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -86,7 +90,9 @@ int CmdRev(const char *Cmd) void clearCommandBuffer() { //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); } /** @@ -95,6 +101,7 @@ void clearCommandBuffer() */ void storeCommand(UsbCommand *command) { + pthread_mutex_lock(&cmdBufferMutex); if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) { //If these two are equal, we're about to overwrite in the @@ -106,6 +113,7 @@ void storeCommand(UsbCommand *command) memcpy(destination, command, sizeof(UsbCommand)); cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); } @@ -116,8 +124,10 @@ void storeCommand(UsbCommand *command) */ int getCommand(UsbCommand* response) { + pthread_mutex_lock(&cmdBufferMutex); //If head == tail, there's nothing to read, or if we just got initialized if(cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); return 0; } //Pick out the next unread command @@ -125,7 +135,7 @@ int getCommand(UsbCommand* response) memcpy(response, last_unread, sizeof(UsbCommand)); //Increment tail - this is a circular buffer, so modulo buffer size cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; - + pthread_mutex_unlock(&cmdBufferMutex); return 1; } @@ -147,22 +157,28 @@ bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeo response = &resp; } + uint64_t start_time = msclock(); + // Wait until the command is received - for(size_t dm_seconds=0; dm_seconds < ms_timeout/10; dm_seconds++) { + while (true) { while(getCommand(response)) { if(response->cmd == cmd){ return true; } } - msleep(10); // XXX ugh - if (dm_seconds == 200 && show_warning) { // Two seconds elapsed + if (msclock() - start_time > ms_timeout) { + break; + } + if (msclock() - start_time > 2000 && show_warning) { PrintAndLog("Waiting for a response from the proxmark..."); PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + break; } } return false; } + bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); } diff --git a/client/proxmark3.c b/client/proxmark3.c index d0e68b6..f7a87f6 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -73,12 +73,11 @@ static void *uart_receiver(void *targ) { while (arg->run) { rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen)) { + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { prx += rxlen; if (prx-rx < sizeof(UsbCommand)) { continue; } - UsbCommandReceived((UsbCommand*)rx); } prx = rx; diff --git a/uart/uart_win32.c b/uart/uart_win32.c index af521ea..121b2b5 100644 --- a/uart/uart_win32.c +++ b/uart/uart_win32.c @@ -107,15 +107,13 @@ void uart_close(const serial_port sp) { free(sp); } -bool uart_receive(const serial_port sp, byte_t* pbtRx, size_t pszMaxRxLen, size_t* pszRxLen) { - ReadFile(((serial_port_windows*)sp)->hPort,pbtRx,pszMaxRxLen,(LPDWORD)pszRxLen,NULL); - return (*pszRxLen != 0); +bool uart_receive(const serial_port sp, byte_t *pbtRx, size_t pszMaxRxLen, size_t *pszRxLen) { + return ReadFile(((serial_port_windows*)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); } bool uart_send(const serial_port sp, const byte_t* pbtTx, const size_t szTxLen) { DWORD dwTxLen = 0; - return WriteFile(((serial_port_windows*)sp)->hPort,pbtTx,szTxLen,&dwTxLen,NULL); - return (dwTxLen != 0); + return WriteFile(((serial_port_windows*)sp)->hPort, pbtTx, szTxLen, &dwTxLen, NULL); } bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { From 8a50d606171df609f7a201f29b0a88e092fa6f40 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Tue, 24 Oct 2017 14:10:52 +0200 Subject: [PATCH 07/19] Flush stdout when waiting for /dev/ttyACM0 --- client/proxmark3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/proxmark3.c b/client/proxmark3.c index f7a87f6..99ba9fb 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -355,11 +355,13 @@ int main(int argc, char* argv[]) { sp = uart_open(argv[1]); } else { printf("Waiting for Proxmark to appear on %s ", argv[1]); + fflush(stdout); int openCount = 0; do { sp = uart_open(argv[1]); msleep(1000); printf("."); + fflush(stdout); } while(++openCount < 20 && (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT)); printf("\n"); } From 8bdb6043b09a32998a2ea16e8aa14bdc815ca7ca Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 25 Oct 2017 08:56:03 +0300 Subject: [PATCH 08/19] identification fix for magic chinese cards (#444) * fixed #411 --- armsrc/mifarecmd.c | 17 ++++++++++++++++- client/mifarehost.c | 19 ++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index edafe0a..39029d4 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1510,6 +1510,16 @@ void MifareCIdent(){ uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); +// FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +// SpinDelay(100); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); ReaderTransmitBitsPar(wupC1,7,0, NULL); if(ReaderReceive(receivedAnswer, receivedAnswerPar) && (receivedAnswer[0] == 0x0a)) { @@ -1523,8 +1533,13 @@ void MifareCIdent(){ // From iceman1001: removed the if, since some magic tags misbehavies and send an answer to it. mifare_classic_halt(NULL, 0); - + + LED_B_ON(); cmd_send(CMD_ACK,isOK,0,0,0,0); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } // diff --git a/client/mifarehost.c b/client/mifarehost.c index a02019a..ca5d97e 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -542,11 +542,12 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID) { int mfCIdentify() { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; - SendCommand(&c); + UsbCommand c; +// UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; +// SendCommand(&c); - UsbCommand resp; - WaitForResponse(CMD_ACK,&resp); + UsbCommand resp; +// WaitForResponse(CMD_ACK,&resp); // iso14a_card_select_t card; // memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); @@ -578,11 +579,11 @@ int mfCIdentify() } // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); +// c.cmd = CMD_READER_ISO_14443a; +// c.arg[0] = 0; +// c.arg[1] = 0; +// c.arg[2] = 0; +// SendCommand(&c); return (int) isGeneration; } From 5d7d0c3af9a97817db86407498f12aae02a085da Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 26 Oct 2017 19:32:18 +0300 Subject: [PATCH 09/19] small improvement in tests (test result in green or red) (#448) --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b072335..c8e58b5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -189,7 +189,6 @@ test_script: #execute scriptblock Write-host "Test [$using:Name] job: $using:Cmd" $Cond=&$sb - Write-host "Result[$using:Name]: $Cond" if ($Cond -eq $null){ } Else { @@ -206,8 +205,10 @@ test_script: } If ($res) { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Green Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" }Else { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Red Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" -ErrorMessage "command:$using:Cmd`nresult:$Cond" } return $res From e17660d5f7dfa44447068e7897ee6454157d2ae1 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Thu, 26 Oct 2017 20:09:53 +0300 Subject: [PATCH 10/19] code cleaning (#445) --- armsrc/mifarecmd.c | 2 -- client/cmdhf14a.c | 21 +-------------------- client/mifarehost.c | 38 +++----------------------------------- 3 files changed, 4 insertions(+), 57 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 39029d4..00fd638 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1514,8 +1514,6 @@ void MifareCIdent(){ LED_A_ON(); LED_B_OFF(); LED_C_OFF(); -// FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); -// SpinDelay(100); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index db9ce46..e3f1a5f 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -405,27 +405,8 @@ int CmdHF14AReader(const char *Cmd) // try to see if card responses to "chinese magic backdoor" commands. - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - WaitForResponse(CMD_ACK,&resp); + mfCIdentify(); - uint8_t isGeneration = resp.arg[0] & 0xff; - switch( isGeneration ){ - case 1: PrintAndLog("Answers to chinese magic backdoor commands (GEN 1a): YES"); break; - case 2: PrintAndLog("Answers to chinese magic backdoor commands (GEN 1b): YES"); break; - default: PrintAndLog("Answers to chinese magic backdoor commands: NO"); break; - } - - // disconnect - c.cmd = CMD_READER_ISO_14443a; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - return select_status; } diff --git a/client/mifarehost.c b/client/mifarehost.c index ca5d97e..fe1a8ed 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -540,35 +540,10 @@ int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID) { return 0; } -int mfCIdentify() -{ - UsbCommand c; -// UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; -// SendCommand(&c); - - UsbCommand resp; -// WaitForResponse(CMD_ACK,&resp); - - // iso14a_card_select_t card; - // memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - - // uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - - // if(select_status != 0) { - // uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - // c.arg[0] = ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT; - // c.arg[1] = 2; - // c.arg[2] = 0; - // memcpy(c.d.asBytes, rats, 2); - // SendCommand(&c); - // WaitForResponse(CMD_ACK,&resp); - // } - - c.cmd = CMD_MIFARE_CIDENT; - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; +int mfCIdentify() { + UsbCommand c = {CMD_MIFARE_CIDENT, {0, 0, 0}}; SendCommand(&c); + UsbCommand resp; WaitForResponse(CMD_ACK,&resp); uint8_t isGeneration = resp.arg[0] & 0xff; @@ -578,13 +553,6 @@ int mfCIdentify() default: PrintAndLog("No chinese magic backdoor command detected"); break; } - // disconnect -// c.cmd = CMD_READER_ISO_14443a; -// c.arg[0] = 0; -// c.arg[1] = 0; -// c.arg[2] = 0; -// SendCommand(&c); - return (int) isGeneration; } From afdcb8c159a73aba95a017f1cfec98e8fa2b93c1 Mon Sep 17 00:00:00 2001 From: Michael Farrell Date: Fri, 27 Oct 2017 06:54:27 +1100 Subject: [PATCH 11/19] Comms refactor (prerequisite of libproxmark work) (#371) * Refactor the comms code only from PR#346, without comms_globals.h. * OSX: Add note for example serial port --- client/Makefile | 10 +- client/cmdlf.c | 8 +- client/cmdmain.c | 152 +------------------ client/cmdmain.h | 7 +- client/cmdparser.c | 3 +- client/comms.c | 325 +++++++++++++++++++++++++++++++++++++++++ client/comms.h | 45 ++++++ client/flash.c | 77 +++++++--- client/flash.h | 5 +- client/flasher.c | 85 ++++------- client/proxgui.cpp | 14 +- client/proxgui.h | 4 +- client/proxguiqt.h | 4 +- client/proxmark3.c | 107 ++++---------- client/proxmark3.h | 3 +- client/ui.c | 12 +- client/ui.h | 8 +- common/iso15693tools.c | 3 +- include/usb_cmd.h | 2 +- 19 files changed, 540 insertions(+), 334 deletions(-) create mode 100644 client/comms.c create mode 100644 client/comms.h diff --git a/client/Makefile b/client/Makefile index 3a96e9e..e14939e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -21,7 +21,7 @@ LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a LDFLAGS = $(ENV_LDFLAGS) CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 -CXXFLAGS = -I../include -Wall -O3 +CXXFLAGS = -I../include -I../uart -Wall -O3 LUAPLATFORM = generic platform = $(shell uname) @@ -82,7 +82,10 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ - util_posix.c + util_posix.c \ + comms.c \ + data.c \ + ui.c CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ @@ -102,9 +105,7 @@ CMDSRCS = crapto1/crapto1.c\ crc64.c \ iso14443crc.c \ iso15693tools.c \ - data.c \ graph.c \ - ui.c \ cmddata.c \ lfdemod.c \ cmdhf.c \ @@ -175,6 +176,7 @@ ZLIBFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED #-DDEBUG -Dverbose=1 QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp +NOGUISRCS = guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) diff --git a/client/cmdlf.c b/client/cmdlf.c index eb664a1..501bfae 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -335,7 +335,7 @@ int CmdLFSetConfig(const char *Cmd) } bool lf_read(bool silent, uint32_t samples) { - if (offline) return false; + if (IsOffline()) return false; UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, {silent,samples,0}}; clearCommandBuffer(); //And ship it to device @@ -878,7 +878,7 @@ int CmdVchDemod(const char *Cmd) int CheckChipType(char cmdp) { uint32_t wordData = 0; - if (offline || cmdp == '1') return 0; + if (IsOffline() || cmdp == '1') return 0; save_restoreGB(GRAPH_SAVE); save_restoreDB(GRAPH_SAVE); @@ -923,7 +923,7 @@ int CmdLFfind(const char *Cmd) return 0; } - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { lf_read(true, 30000); } else if (GraphTraceLen < minLength) { PrintAndLog("Data in Graphbuffer was too small."); @@ -939,7 +939,7 @@ int CmdLFfind(const char *Cmd) // only run if graphbuffer is just noise as it should be for hitag/cotag if (graphJustNoise(GraphBuffer, testLen)) { // only run these tests if we are in online mode - if (!offline && (cmdp != '1')) { + if (!IsOffline() && (cmdp != '1')) { // test for em4x05 in reader talk first mode. if (EM4x05Block0Test(&wordData)) { PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); diff --git a/client/cmdmain.c b/client/cmdmain.c index 739d68e..e199e12 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -36,15 +36,6 @@ static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); static int CmdRev(const char *Cmd); -//For storing command that are received from the device -#define CMD_BUFFER_SIZE 50 -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; -//Points to the next empty position to write to -static int cmd_head;//Starts as 0 -//Points to the position of the last unread command -static int cmd_tail;//Starts as 0 -// to lock cmdBuffer operations from different threads -static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -64,6 +55,7 @@ command_t* getTopLevelCommandTable() { return CommandTable; } + int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); @@ -81,113 +73,6 @@ int CmdRev(const char *Cmd) return 0; } -/** - * @brief This method should be called when sending a new command to the pm3. In case any old - * responses from previous commands are stored in the buffer, a call to this method should clear them. - * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which - * operation. Right now we'll just have to live with this. - */ -void clearCommandBuffer() -{ - //This is a very simple operation - pthread_mutex_lock(&cmdBufferMutex); - cmd_tail = cmd_head; - pthread_mutex_unlock(&cmdBufferMutex); -} - -/** - * @brief storeCommand stores a USB command in a circular buffer - * @param UC - */ -void storeCommand(UsbCommand *command) -{ - pthread_mutex_lock(&cmdBufferMutex); - if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) - { - //If these two are equal, we're about to overwrite in the - // circular buffer. - PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); - } - //Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; - memcpy(destination, command, sizeof(UsbCommand)); - - cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap - pthread_mutex_unlock(&cmdBufferMutex); -} - - -/** - * @brief getCommand gets a command from an internal circular buffer. - * @param response location to write command - * @return 1 if response was returned, 0 if nothing has been received - */ -int getCommand(UsbCommand* response) -{ - pthread_mutex_lock(&cmdBufferMutex); - //If head == tail, there's nothing to read, or if we just got initialized - if(cmd_head == cmd_tail){ - pthread_mutex_unlock(&cmdBufferMutex); - return 0; - } - //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; - memcpy(response, last_unread, sizeof(UsbCommand)); - //Increment tail - this is a circular buffer, so modulo buffer size - cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; - pthread_mutex_unlock(&cmdBufferMutex); - return 1; -} - - -/** - * Waits for a certain response type. This method waits for a maximum of - * ms_timeout milliseconds for a specified response command. - *@brief WaitForResponseTimeout - * @param cmd command to wait for - * @param response struct to copy received command into. - * @param ms_timeout - * @return true if command was returned, otherwise false - */ -bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { - - UsbCommand resp; - - if (response == NULL) { - response = &resp; - } - - uint64_t start_time = msclock(); - - // Wait until the command is received - while (true) { - while(getCommand(response)) { - if(response->cmd == cmd){ - return true; - } - } - if (msclock() - start_time > ms_timeout) { - break; - } - if (msclock() - start_time > 2000 && show_warning) { - PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); - break; - } - } - return false; -} - - -bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { - return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); -} - -bool WaitForResponse(uint32_t cmd, UsbCommand* response) { - return WaitForResponseTimeoutW(cmd, response, -1, true); -} - - //----------------------------------------------------------------------------- // Entry point into our code: called whenever the user types a command and // then presses Enter, which the full command line that they typed. @@ -196,38 +81,3 @@ int CommandReceived(char *Cmd) { return CmdsParse(CommandTable, Cmd); } - -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) -{ - switch(UC->cmd) { - // First check if we are handling a debug message - case CMD_DEBUG_PRINT_STRING: { - char s[USB_CMD_DATA_SIZE+1]; - memset(s, 0x00, sizeof(s)); - size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); - memcpy(s,UC->d.asBytes,len); - PrintAndLog("#db# %s", s); - return; - } break; - - case CMD_DEBUG_PRINT_INTEGERS: { - PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); - return; - } break; - - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - return; - } break; - - default: - storeCommand(UC); - break; - } - -} - diff --git a/client/cmdmain.h b/client/cmdmain.h index d39bc11..a833b41 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -15,13 +15,10 @@ #include #include "usb_cmd.h" #include "cmdparser.h" +#include "comms.h" + -extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); -extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); -extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); -extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); -extern void clearCommandBuffer(); extern command_t* getTopLevelCommandTable(); #endif diff --git a/client/cmdparser.c b/client/cmdparser.c index 3250899..28ca219 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -14,6 +14,7 @@ #include "ui.h" #include "cmdparser.h" #include "proxmark3.h" +#include "comms.h" void CmdsHelp(const command_t Commands[]) @@ -23,7 +24,7 @@ void CmdsHelp(const command_t Commands[]) int i = 0; while (Commands[i].Name) { - if (!offline || Commands[i].Offline) + if (!IsOffline() || Commands[i].Offline) PrintAndLog("%-16s %s", Commands[i].Name, Commands[i].Help); ++i; } diff --git a/client/comms.c b/client/comms.c new file mode 100644 index 0000000..abbae38 --- /dev/null +++ b/client/comms.c @@ -0,0 +1,325 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Code for communicating with the proxmark3 hardware. +//----------------------------------------------------------------------------- + +#include + +#include "comms.h" +#include "uart.h" +#include "ui.h" +#include "common.h" +#include "data.h" +#include "util_posix.h" + +// Declare globals. + +// Serial port that we are communicating with the PM3 on. +static serial_port* port; + +// If TRUE, then there is no active connection to the PM3, and we will drop commands sent. +static bool offline; + +// Transmit buffer. +// TODO: Use locks and execute this on the main thread, rather than the receiver +// thread. Running on the main thread means we need to be careful in the +// flasher, as it means SendCommand is no longer async, and can't be used as a +// buffer for a pending command when the connection is re-established. +static UsbCommand txcmd; +static bool txcmd_pending; + +// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be +// processed by a command handler (WaitForResponse{,Timeout}) +static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; + +// Points to the next empty position to write to +static int cmd_head = 0; + +// Points to the position of the last unread command +static int cmd_tail = 0; + +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; + +// These wrappers are required because it is not possible to access a static +// global variable outside of the context of a single file. + +void SetSerialPort(serial_port* new_port) { + port = new_port; +} + +serial_port* GetSerialPort() { + return port; +} + +void SetOffline(bool new_offline) { + offline = new_offline; +} + +bool IsOffline() { + return offline; +} + +void SendCommand(UsbCommand *c) { + #ifdef COMMS_DEBUG + printf("Sending %04x cmd\n", c->cmd); + #endif + + if (offline) { + PrintAndLog("Sending bytes to proxmark failed - offline"); + return; + } + /** + The while-loop below causes hangups at times, when the pm3 unit is unresponsive + or disconnected. The main console thread is alive, but comm thread just spins here. + Not good.../holiman + **/ + while(txcmd_pending); + txcmd = *c; + txcmd_pending = true; +} + +/** + * @brief This method should be called when sending a new command to the pm3. In case any old + * responses from previous commands are stored in the buffer, a call to this method should clear them. + * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which + * operation. Right now we'll just have to live with this. + */ +void clearCommandBuffer() +{ + //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); + cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); +} + +/** + * @brief storeCommand stores a USB command in a circular buffer + * @param UC + */ +void storeCommand(UsbCommand *command) +{ + pthread_mutex_lock(&cmdBufferMutex); + if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) + { + // If these two are equal, we're about to overwrite in the + // circular buffer. + PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); + } + + // Store the command at the 'head' location + UsbCommand* destination = &cmdBuffer[cmd_head]; + memcpy(destination, command, sizeof(UsbCommand)); + + cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); +} + + +/** + * @brief getCommand gets a command from an internal circular buffer. + * @param response location to write command + * @return 1 if response was returned, 0 if nothing has been received + */ +int getCommand(UsbCommand* response) +{ + pthread_mutex_lock(&cmdBufferMutex); + //If head == tail, there's nothing to read, or if we just got initialized + if (cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); + return 0; + } + + //Pick out the next unread command + UsbCommand* last_unread = &cmdBuffer[cmd_tail]; + memcpy(response, last_unread, sizeof(UsbCommand)); + //Increment tail - this is a circular buffer, so modulo buffer size + cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; + + pthread_mutex_unlock(&cmdBufferMutex); + return 1; +} + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB +// that we weren't necessarily expecting, for example a debug print. +//----------------------------------------------------------------------------- +void UsbCommandReceived(UsbCommand *UC) +{ + switch(UC->cmd) { + + // First check if we are handling a debug message + case CMD_DEBUG_PRINT_STRING: { + char s[USB_CMD_DATA_SIZE+1]; + memset(s, 0x00, sizeof(s)); + size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); + memcpy(s,UC->d.asBytes,len); + PrintAndLog("#db# %s", s); + return; + } + + case CMD_DEBUG_PRINT_INTEGERS: { + PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); + return; + } + + case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { + // FIXME: This does unsanitised copies into memory when we don't know + // the size of the buffer. + if (sample_buf) { + memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); + } + return; + } + + default: { + storeCommand(UC); + return; + } + } +} + +// Gets a single command from a proxmark3 device. This should never be used +// with the full client. +// +// @param conn A receiver_arg structure. +// @param command A buffer to store the received command. +bool ReceiveCommand(receiver_arg* conn, UsbCommand* command) { + // Local recieve buffer + size_t rxlen; + byte_t rx[sizeof(UsbCommand)]; + byte_t* prx = rx; + + while (conn->run) { + rxlen = 0; + if (uart_receive(port, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { + prx += rxlen; + if (prx-rx < sizeof(UsbCommand)) { + // Keep reading until we have a completed response. + continue; + } + + // We have a completed response. + memcpy(command, rx, sizeof(UsbCommand)); + return true; + } + + if (prx == rx) { + // We got no complete command while waiting, give up control + return false; + } + } + + // did not get a complete command before being cancelled. + return false; +} + +// Worker thread for processing incoming events from the PM3 +void *uart_receiver(void *targ) { + receiver_arg *conn = (receiver_arg*)targ; + UsbCommand rx; + + while (conn->run) { + #ifdef COMMS_DEBUG + printf("uart_receiver: get lock\n"); + #endif + // Lock up receives, in case they try to take it away from us. + pthread_mutex_lock(&conn->recv_lock); + #ifdef COMMS_DEBUG + printf("uart_receiver: lock acquired\n"); + #endif + + if (port == NULL) { + #ifdef COMMS_DEBUG + printf("uart_receiver: port disappeared\n"); + #endif + // Our port disappeared, stall. This code path matters for the flasher, + // where it is fiddling with the serial port under us. + pthread_mutex_unlock(&conn->recv_lock); + msleep(10); + continue; + } + + bool got_command = ReceiveCommand(conn, &rx); + #ifdef COMMS_DEBUG + printf("uart_receiver: got command\n"); + #endif + pthread_mutex_unlock(&conn->recv_lock); + + if (got_command) { + UsbCommandReceived(&rx); + } + + // We aren't normally trying to transmit in the flasher when the port would + // be reset, so we can just keep going at this point. + if (txcmd_pending) { + if (!uart_send(port, (byte_t*) &txcmd, sizeof(UsbCommand))) { + PrintAndLog("Sending bytes to proxmark failed"); + } + txcmd_pending = false; + } + } + + pthread_exit(NULL); + return NULL; +} + +/** + * Waits for a certain response type. This method waits for a maximum of + * ms_timeout milliseconds for a specified response command. + *@brief WaitForResponseTimeout + * @param cmd command to wait for, or CMD_ANY to take any command. + * @param response struct to copy received command into. + * @param ms_timeout + * @param show_warning + * @return true if command was returned, otherwise false + */ +bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { + UsbCommand resp; + + #ifdef COMMS_DEBUG + printf("Waiting for %04x cmd\n", cmd); + #endif + + if (response == NULL) { + response = &resp; + } + + uint64_t start_time = msclock(); + + // Wait until the command is received + for (;;) { + while(getCommand(response)) { + if (cmd == CMD_ANY || response->cmd == cmd) { + return true; + } + } + + if (msclock() - start_time > ms_timeout) { + // We timed out. + break; + } + + if (msclock() - start_time > 2000 && show_warning) { + // 2 seconds elapsed (but this doesn't mean the timeout was exceeded) + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + break; + } + } + return false; +} + +bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} + +bool WaitForResponse(uint64_t cmd, UsbCommand* response) { + return WaitForResponseTimeout(cmd, response, -1); +} diff --git a/client/comms.h b/client/comms.h new file mode 100644 index 0000000..75adeea --- /dev/null +++ b/client/comms.h @@ -0,0 +1,45 @@ +#ifndef COMMS_H_ +#define COMMS_H_ + +#include +#include + +#include "usb_cmd.h" +#include "uart.h" + +#ifndef CMD_BUFFER_SIZE +#define CMD_BUFFER_SIZE 50 +#endif + +#ifndef MAX_DEMOD_BUF_LEN +#define MAX_DEMOD_BUF_LEN (1024*128) +#endif + +#ifndef BIGBUF_SIZE +#define BIGBUF_SIZE 40000 +#endif + +typedef struct { + // If TRUE, continue running the uart_receiver thread. + bool run; + + // Lock around serial port receives + pthread_mutex_t recv_lock; +} receiver_arg; + + +// Wrappers required as static variables can only be used in one file. +void SetSerialPort(serial_port* new_port); +serial_port* GetSerialPort(); +void SetOffline(bool new_offline); +bool IsOffline(); + +void SendCommand(UsbCommand *c); +void *uart_receiver(void *targ); +void UsbCommandReceived(UsbCommand *UC); +void clearCommandBuffer(); +bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); +bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout); +bool WaitForResponse(uint64_t cmd, UsbCommand* response); + +#endif // COMMS_H_ diff --git a/client/flash.c b/client/flash.c index 7622e8a..bf01b91 100644 --- a/client/flash.c +++ b/client/flash.c @@ -13,6 +13,7 @@ #include #include #include + #include "proxmark3.h" #include "util.h" #include "util_posix.h" @@ -20,11 +21,7 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" - -void SendCommand(UsbCommand* txcmd); -void ReceiveCommand(UsbCommand* rxcmd); -void CloseProxmark(); -int OpenProxmark(size_t i); +#include "comms.h" // FIXME: what the fuckity fuck unsigned int current_command = CMD_UNKNOWN; @@ -44,6 +41,32 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; +void CloseProxmark(receiver_arg* conn, char* serial_port_name) { + pthread_mutex_lock(&conn->recv_lock); + + // Block the port from being used by anything + serial_port* my_port = GetSerialPort(); + SetSerialPort(NULL); + + // Then close the port. + uart_close(my_port); + pthread_mutex_unlock(&conn->recv_lock); + + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + unlink(serial_port_name); +} + +bool OpenProxmark(char* serial_port_name) { + serial_port *new_port = uart_open(serial_port_name); + if (new_port == INVALID_SERIAL_PORT || new_port == CLAIMED_SERIAL_PORT) { + //poll once a second + return false; + } + + SetSerialPort(new_port); + return true; +} + // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) @@ -278,9 +301,12 @@ static int get_proxmark_state(uint32_t *state) { UsbCommand c; c.cmd = CMD_DEVICE_INFO; - SendCommand(&c); + SendCommand(&c); UsbCommand resp; - ReceiveCommand(&resp); + while (!WaitForResponse(CMD_ANY, &resp)) { + // Keep waiting for a response + msleep(100); + } // Three outcomes: // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK @@ -307,7 +333,7 @@ static int get_proxmark_state(uint32_t *state) } // Enter the bootloader to be able to start flashing -static int enter_bootloader(char *serial_port_name) +static int enter_bootloader(receiver_arg* conn, char *serial_port_name) { uint32_t state; @@ -338,16 +364,17 @@ static int enter_bootloader(char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } - msleep(100); - CloseProxmark(); + + msleep(100); + CloseProxmark(conn, serial_port_name); fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { + do { sleep(1); fprintf(stderr, "."); - } while (!OpenProxmark(0)); + } while (!OpenProxmark(serial_port_name)); + fprintf(stderr," Found.\n"); - return 0; } @@ -355,23 +382,25 @@ static int enter_bootloader(char *serial_port_name) return -1; } -static int wait_for_ack(void) +static int wait_for_ack() { - UsbCommand ack; - ReceiveCommand(&ack); - if (ack.cmd != CMD_ACK) { - printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); + UsbCommand resp; + while (!WaitForResponse(CMD_ANY, &resp)) { + msleep(100); + } + if (resp.cmd != CMD_ACK) { + printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", resp.cmd); return -1; } return 0; } // Go into flashing mode -int flash_start_flashing(int enable_bl_writes,char *serial_port_name) +int flash_start_flashing(receiver_arg* conn, int enable_bl_writes,char *serial_port_name) { uint32_t state; - if (enter_bootloader(serial_port_name) < 0) + if (enter_bootloader(conn, serial_port_name) < 0) return -1; if (get_proxmark_state(&state) < 0) @@ -470,9 +499,9 @@ void flash_free(flash_file_t *ctx) } // just reset the unit -int flash_stop_flashing(void) { +int flash_stop_flashing() { UsbCommand c = {CMD_HARDWARE_RESET}; - SendCommand(&c); - msleep(100); - return 0; + SendCommand(&c); + msleep(100); + return 0; } diff --git a/client/flash.h b/client/flash.h index 3e9f77a..279062a 100644 --- a/client/flash.h +++ b/client/flash.h @@ -11,6 +11,7 @@ #include #include "elf.h" +#include "comms.h" typedef struct { void *data; @@ -26,10 +27,12 @@ typedef struct { } flash_file_t; int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); -int flash_start_flashing(int enable_bl_writes,char *serial_port_name); +int flash_start_flashing(receiver_arg* conn, int enable_bl_writes, char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); +void CloseProxmark(receiver_arg* conn, char* serial_port_name); +bool OpenProxmark(char* serial_port_name); #endif diff --git a/client/flasher.c b/client/flasher.c index f257d99..40e4052 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -10,12 +10,15 @@ #include #include #include +#include + #include "proxmark3.h" #include "util.h" #include "util_posix.h" #include "flash.h" #include "uart.h" #include "usb_cmd.h" +#include "comms.h" #ifdef _WIN32 # define unlink(x) @@ -23,9 +26,6 @@ # include #endif -static serial_port sp; -static char* serial_port_name; - void cmd_debug(UsbCommand* UC) { // Debug printf("UsbCommand length[len=%zd]\n",sizeof(UsbCommand)); @@ -40,45 +40,6 @@ void cmd_debug(UsbCommand* UC) { printf("...\n"); } -void SendCommand(UsbCommand* txcmd) { -// printf("send: "); -// cmd_debug(txcmd); - if (!uart_send(sp,(byte_t*)txcmd,sizeof(UsbCommand))) { - printf("Sending bytes to proxmark failed\n"); - exit(1); - } -} - -void ReceiveCommand(UsbCommand* rxcmd) { - byte_t* prxcmd = (byte_t*)rxcmd; - byte_t* prx = prxcmd; - size_t rxlen; - while (true) { - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { - prx += rxlen; - if ((prx-prxcmd) >= sizeof(UsbCommand)) { - return; - } - } - } -} - -void CloseProxmark() { - // Clean up the port - uart_close(sp); - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -int OpenProxmark(size_t i) { - sp = uart_open(serial_port_name); - if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { - //poll once a second - return 0; - } - return 1; -} - static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); @@ -86,9 +47,10 @@ static void usage(char *argv0) //Is the example below really true? /Martin fprintf(stderr, "Example:\n\n\t %s path/to/osimage.elf path/to/fpgaimage.elf\n", argv0); fprintf(stderr, "\nExample (Linux):\n\n\t %s /dev/ttyACM0 armsrc/obj/fullimage.elf\n", argv0); - fprintf(stderr, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark to reappear on ',\n"); - fprintf(stderr, " you need to blacklist proxmark for modem-manager - see wiki for more details:\n"); - fprintf(stderr, " http://code.google.com/p/proxmark3/wiki/Linux\n\n"); + fprintf(stderr, "\nNote (Linux): if the flasher gets stuck at 'Waiting for Proxmark to reappear',\n"); + fprintf(stderr, " you may need to blacklist proxmark for modem-manager. v1.4.14 and later\n"); + fprintf(stderr, " include this configuration patch already. The change can be found at:\n"); + fprintf(stderr, " https://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=6e7ff47\n\n"); } #define MAX_FILES 4 @@ -99,7 +61,10 @@ int main(int argc, char **argv) int num_files = 0; int res; flash_file_t files[MAX_FILES]; + receiver_arg conn; + pthread_t reader_thread; + memset(&conn, 0, sizeof(receiver_arg)); memset(files, 0, sizeof(files)); if (argc < 3) { @@ -126,16 +91,22 @@ int main(int argc, char **argv) } } - serial_port_name = argv[1]; - - fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); - do { - msleep(1000); - fprintf(stderr, "."); - } while (!OpenProxmark(0)); - fprintf(stderr," Found.\n"); + pthread_mutex_init(&conn.recv_lock, NULL); - res = flash_start_flashing(can_write_bl,serial_port_name); + char* serial_port_name = argv[1]; + + fprintf(stderr,"Waiting for Proxmark to appear on %s", serial_port_name); + do { + sleep(1); + fprintf(stderr, "."); + } while (!OpenProxmark(serial_port_name)); + fprintf(stderr," Found.\n"); + + // Lets start up the communications thread + conn.run = true; + pthread_create(&reader_thread, NULL, &uart_receiver, &conn); + + res = flash_start_flashing(&conn, can_write_bl, serial_port_name); if (res < 0) return -1; @@ -155,7 +126,11 @@ int main(int argc, char **argv) if (res < 0) return -1; - CloseProxmark(); + // Stop the command thread. + conn.run = false; + pthread_join(reader_thread, NULL); + CloseProxmark(&conn, serial_port_name); + pthread_mutex_destroy(&conn.recv_lock); fprintf(stderr, "All done.\n\n"); fprintf(stderr, "Have a nice day!\n"); diff --git a/client/proxgui.cpp b/client/proxgui.cpp index e899174..6e4ec98 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -11,11 +11,15 @@ #include "proxgui.h" #include "proxguiqt.h" #include "proxmark3.h" +#include "uart.h" static ProxGuiQT *gui = NULL; static WorkerThread *main_loop_thread = NULL; -WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool usb_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), usb_present(usb_present) +WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, + bool usb_present, serial_port* sp) + : script_cmds_file(script_cmds_file), script_cmd(script_cmd), + usb_present(usb_present), sp(sp) { } @@ -24,7 +28,7 @@ WorkerThread::~WorkerThread() } void WorkerThread::run() { - main_loop(script_cmds_file, script_cmd, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present, sp); } extern "C" void ShowGraphWindow(void) @@ -59,7 +63,9 @@ extern "C" void MainGraphics(void) gui->MainLoop(); } -extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) +extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, + char *script_cmd, bool usb_present, + serial_port* sp) { #ifdef Q_WS_X11 bool useGUI = getenv("DISPLAY") != 0; @@ -69,7 +75,7 @@ extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char if (!useGUI) return; - main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); + main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present, sp); gui = new ProxGuiQT(argc, argv, main_loop_thread); } diff --git a/client/proxgui.h b/client/proxgui.h index 77bcbf0..ea3abf3 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -14,12 +14,13 @@ extern "C" { #include #include +#include "uart.h" void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); void MainGraphics(void); -void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present); +void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); void ExitGraphics(void); #define MAX_GRAPH_TRACE_LEN (40000*8) @@ -30,7 +31,6 @@ extern int s_Buff[MAX_GRAPH_TRACE_LEN]; extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; extern int CommandFinished; -extern int offline; extern bool GridLocked; //Operations defined in data_operations diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 45a65b0..4b2b2a2 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -21,6 +21,7 @@ #include #include +#include "uart.h" #include "ui/ui_overlays.h" /** * @brief The actual plot, black area were we paint the graph @@ -91,13 +92,14 @@ class ProxWidget : public QWidget class WorkerThread : public QThread { Q_OBJECT; public: - WorkerThread(char*, char*, bool); + WorkerThread(char*, char*, bool, serial_port*); ~WorkerThread(); void run(); private: char *script_cmds_file = NULL; char *script_cmd = NULL; bool usb_present; + serial_port *sp = NULL; }; class ProxGuiQT : public QObject diff --git a/client/proxmark3.c b/client/proxmark3.c index 99ba9fb..0acf34f 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -27,86 +27,42 @@ #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" +#include "comms.h" #ifdef _WIN32 #define SERIAL_PORT_H "com3" +#elif __APPLE__ +#define SERIAL_PORT_H "/dev/tty.usbmodem*" #else #define SERIAL_PORT_H "/dev/ttyACM0" #endif -// a global mutex to prevent interlaced printing from different threads -pthread_mutex_t print_lock; - -static serial_port sp; -static UsbCommand txcmd; -volatile static bool txcmd_pending = false; - -void SendCommand(UsbCommand *c) { - #if 0 - printf("Sending %d bytes\n", sizeof(UsbCommand)); - #endif - - if (offline) { - PrintAndLog("Sending bytes to proxmark failed - offline"); - return; - } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; -} - -struct receiver_arg { - int run; -}; - -byte_t rx[sizeof(UsbCommand)]; -byte_t* prx = rx; - -static void *uart_receiver(void *targ) { - struct receiver_arg *arg = (struct receiver_arg*)targ; - size_t rxlen; - - while (arg->run) { - rxlen = 0; - if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { - prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { - continue; - } - UsbCommandReceived((UsbCommand*)rx); - } - prx = rx; - - if(txcmd_pending) { - if (!uart_send(sp, (byte_t*) &txcmd, sizeof(UsbCommand))) { - PrintAndLog("Sending bytes to proxmark failed"); - } - txcmd_pending = false; - } - } - - pthread_exit(NULL); - return NULL; -} - - -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { - struct receiver_arg rarg; +void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, serial_port* sp) { + receiver_arg conn; char *cmd = NULL; pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); - + + memset(&conn, 0, sizeof(receiver_arg)); + pthread_mutex_init(&conn.recv_lock, NULL); + + + // TODO: Move this into comms.c + PlotGridXdefault = 64; + PlotGridYdefault = 64; + showDemod = true; + CursorScaleFactor = 1; + if (usb_present) { - rarg.run = 1; - pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); + conn.run = true; + SetSerialPort(sp); + SetOffline(false); + pthread_create(&reader_thread, NULL, &uart_receiver, &conn); // cache Version information now: CmdVersion(NULL); + } else { + SetOffline(true); } // file with script @@ -122,7 +78,7 @@ void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { read_history(".history"); - while(1) { + while (1) { // If there is a script file if (script_file) { @@ -194,7 +150,7 @@ void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { write_history(".history"); if (usb_present) { - rarg.run = 0; + conn.run = false; pthread_join(reader_thread, NULL); } @@ -202,6 +158,8 @@ void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { fclose(script_file); script_file = NULL; } + + pthread_mutex_destroy(&conn.recv_lock); } static void dumpAllHelp(int markdown) @@ -274,6 +232,8 @@ int main(int argc, char* argv[]) { bool addLuaExec = false; char *script_cmds_file = NULL; char *script_cmd = NULL; + serial_port *sp = NULL; + g_debugMode = 0; if (argc < 2) { show_help(true, argv[0]); @@ -370,14 +330,11 @@ int main(int argc, char* argv[]) { if (sp == INVALID_SERIAL_PORT) { printf("ERROR: invalid serial port\n"); usb_present = false; - offline = 1; } else if (sp == CLAIMED_SERIAL_PORT) { printf("ERROR: serial port is claimed by another process\n"); usb_present = false; - offline = 1; } else { usb_present = true; - offline = 0; } // create a mutex to avoid interlacing print commands from our different threads @@ -385,23 +342,23 @@ int main(int argc, char* argv[]) { #ifdef HAVE_GUI #ifdef _WIN32 - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); MainGraphics(); #else char* display = getenv("DISPLAY"); if (display && strlen(display) > 1) { - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); MainGraphics(); } else { - main_loop(script_cmds_file, script_cmd, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present, sp); } #endif #else - main_loop(script_cmds_file, script_cmd, usb_present); + main_loop(script_cmds_file, script_cmd, usb_present, sp); #endif // Clean up the port diff --git a/client/proxmark3.h b/client/proxmark3.h index c6185c4..11b8aab 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -13,6 +13,7 @@ #define PROXMARK3_H__ #include "usb_cmd.h" +#include "uart.h" #define PROXPROMPT "proxmark3> " @@ -23,7 +24,7 @@ extern "C" { void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); #ifdef __cplusplus } diff --git a/client/ui.c b/client/ui.c index df2c3ce..48869fd 100644 --- a/client/ui.c +++ b/client/ui.c @@ -21,7 +21,7 @@ double CursorScaleFactor = 1; int PlotGridX=0, PlotGridY=0, PlotGridXdefault= 64, PlotGridYdefault= 64, CursorCPos= 0, CursorDPos= 0; int offline; -int flushAfterWrite = 0; //buzzy +bool flushAfterWrite = false; //buzzy int GridOffset = 0; bool GridLocked = false; bool showDemod = true; @@ -62,7 +62,6 @@ void PrintAndLog(char *fmt, ...) } #else // We are using libedit (OSX), which doesn't support this flag. - int need_hack = 0; #endif va_start(argptr, fmt); @@ -72,6 +71,9 @@ void PrintAndLog(char *fmt, ...) va_end(argptr); printf("\n"); + // This needs to be wrapped in ifdefs, as this if optimisation is disabled, + // this block won't be removed, and it'll fail at the linker. +#ifdef RL_STATE_READCMD if (need_hack) { rl_restore_prompt(); rl_replace_line(saved_line, 0); @@ -79,6 +81,7 @@ void PrintAndLog(char *fmt, ...) rl_redisplay(); free(saved_line); } +#endif if (logging && logfile) { vfprintf(logfile, fmt, argptr2); @@ -100,3 +103,8 @@ void SetLogFilename(char *fn) { logfilename = fn; } + +void SetFlushAfterWrite(bool flush_after_write) { + flushAfterWrite = flush_after_write; +} + diff --git a/client/ui.h b/client/ui.h index 4049033..2986efa 100644 --- a/client/ui.h +++ b/client/ui.h @@ -13,6 +13,11 @@ #include #include +#include + +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; +extern uint8_t g_debugMode; void ShowGui(void); void HideGraphWindow(void); @@ -23,8 +28,7 @@ void SetLogFilename(char *fn); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern int offline; -extern int flushAfterWrite; //buzzy +extern bool flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/common/iso15693tools.c b/common/iso15693tools.c index 26e636c..cf7d534 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -50,8 +50,9 @@ int Iso15693AddCrc(uint8_t *req, int n) { return n+2; } - +#ifdef ON_DEVICE int sprintf(char *str, const char *format, ...); +#endif // returns a string representation of the UID // UID is transmitted and stored LSB first, displayed MSB first diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 194a9d5..df2923a 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -212,7 +212,7 @@ typedef struct{ #define CMD_HF_SNIFFER 0x0800 #define CMD_UNKNOWN 0xFFFF - +#define CMD_ANY 0xFFFFFFFFFFFFFFFF //Mifare simulation flags #define FLAG_INTERACTIVE 0x01 From 3851172d8168f32f1dc28b48b7a4df88e30bdeca Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 27 Oct 2017 09:56:46 +0200 Subject: [PATCH 12/19] Revert "Comms refactor (prerequisite of libproxmark work) (#371)" (#450) This reverts commit afdcb8c159a73aba95a017f1cfec98e8fa2b93c1. --- client/Makefile | 10 +- client/cmdlf.c | 8 +- client/cmdmain.c | 152 ++++++++++++++++++- client/cmdmain.h | 7 +- client/cmdparser.c | 3 +- client/comms.c | 325 ----------------------------------------- client/comms.h | 45 ------ client/flash.c | 77 +++------- client/flash.h | 5 +- client/flasher.c | 85 +++++++---- client/proxgui.cpp | 14 +- client/proxgui.h | 4 +- client/proxguiqt.h | 4 +- client/proxmark3.c | 107 ++++++++++---- client/proxmark3.h | 3 +- client/ui.c | 12 +- client/ui.h | 8 +- common/iso15693tools.c | 3 +- include/usb_cmd.h | 2 +- 19 files changed, 334 insertions(+), 540 deletions(-) delete mode 100644 client/comms.c delete mode 100644 client/comms.h diff --git a/client/Makefile b/client/Makefile index e14939e..3a96e9e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -21,7 +21,7 @@ LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm LUALIB = ../liblua/liblua.a LDFLAGS = $(ENV_LDFLAGS) CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -I. -I../include -I../common -I../zlib -I../uart -I/opt/local/include -I../liblua -Wall -g -O3 -CXXFLAGS = -I../include -I../uart -Wall -O3 +CXXFLAGS = -I../include -Wall -O3 LUAPLATFORM = generic platform = $(shell uname) @@ -82,10 +82,7 @@ POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d CORESRCS = uart_posix.c \ uart_win32.c \ util.c \ - util_posix.c \ - comms.c \ - data.c \ - ui.c + util_posix.c CMDSRCS = crapto1/crapto1.c\ crapto1/crypto1.c\ @@ -105,7 +102,9 @@ CMDSRCS = crapto1/crapto1.c\ crc64.c \ iso14443crc.c \ iso15693tools.c \ + data.c \ graph.c \ + ui.c \ cmddata.c \ lfdemod.c \ cmdhf.c \ @@ -176,7 +175,6 @@ ZLIBFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED #-DDEBUG -Dverbose=1 QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp -NOGUISRCS = guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) diff --git a/client/cmdlf.c b/client/cmdlf.c index 501bfae..eb664a1 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -335,7 +335,7 @@ int CmdLFSetConfig(const char *Cmd) } bool lf_read(bool silent, uint32_t samples) { - if (IsOffline()) return false; + if (offline) return false; UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, {silent,samples,0}}; clearCommandBuffer(); //And ship it to device @@ -878,7 +878,7 @@ int CmdVchDemod(const char *Cmd) int CheckChipType(char cmdp) { uint32_t wordData = 0; - if (IsOffline() || cmdp == '1') return 0; + if (offline || cmdp == '1') return 0; save_restoreGB(GRAPH_SAVE); save_restoreDB(GRAPH_SAVE); @@ -923,7 +923,7 @@ int CmdLFfind(const char *Cmd) return 0; } - if (!IsOffline() && (cmdp != '1')) { + if (!offline && (cmdp != '1')) { lf_read(true, 30000); } else if (GraphTraceLen < minLength) { PrintAndLog("Data in Graphbuffer was too small."); @@ -939,7 +939,7 @@ int CmdLFfind(const char *Cmd) // only run if graphbuffer is just noise as it should be for hitag/cotag if (graphJustNoise(GraphBuffer, testLen)) { // only run these tests if we are in online mode - if (!IsOffline() && (cmdp != '1')) { + if (!offline && (cmdp != '1')) { // test for em4x05 in reader talk first mode. if (EM4x05Block0Test(&wordData)) { PrintAndLog("\nValid EM4x05/EM4x69 Chip Found\nUse lf em 4x05readword/dump commands to read\n"); diff --git a/client/cmdmain.c b/client/cmdmain.c index e199e12..739d68e 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -36,6 +36,15 @@ static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); static int CmdRev(const char *Cmd); +//For storing command that are received from the device +#define CMD_BUFFER_SIZE 50 +static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; +//Points to the next empty position to write to +static int cmd_head;//Starts as 0 +//Points to the position of the last unread command +static int cmd_tail;//Starts as 0 +// to lock cmdBuffer operations from different threads +static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; static command_t CommandTable[] = { @@ -55,7 +64,6 @@ command_t* getTopLevelCommandTable() { return CommandTable; } - int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); @@ -73,6 +81,113 @@ int CmdRev(const char *Cmd) return 0; } +/** + * @brief This method should be called when sending a new command to the pm3. In case any old + * responses from previous commands are stored in the buffer, a call to this method should clear them. + * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which + * operation. Right now we'll just have to live with this. + */ +void clearCommandBuffer() +{ + //This is a very simple operation + pthread_mutex_lock(&cmdBufferMutex); + cmd_tail = cmd_head; + pthread_mutex_unlock(&cmdBufferMutex); +} + +/** + * @brief storeCommand stores a USB command in a circular buffer + * @param UC + */ +void storeCommand(UsbCommand *command) +{ + pthread_mutex_lock(&cmdBufferMutex); + if( ( cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) + { + //If these two are equal, we're about to overwrite in the + // circular buffer. + PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); + } + //Store the command at the 'head' location + UsbCommand* destination = &cmdBuffer[cmd_head]; + memcpy(destination, command, sizeof(UsbCommand)); + + cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap + pthread_mutex_unlock(&cmdBufferMutex); +} + + +/** + * @brief getCommand gets a command from an internal circular buffer. + * @param response location to write command + * @return 1 if response was returned, 0 if nothing has been received + */ +int getCommand(UsbCommand* response) +{ + pthread_mutex_lock(&cmdBufferMutex); + //If head == tail, there's nothing to read, or if we just got initialized + if(cmd_head == cmd_tail){ + pthread_mutex_unlock(&cmdBufferMutex); + return 0; + } + //Pick out the next unread command + UsbCommand* last_unread = &cmdBuffer[cmd_tail]; + memcpy(response, last_unread, sizeof(UsbCommand)); + //Increment tail - this is a circular buffer, so modulo buffer size + cmd_tail = (cmd_tail +1 ) % CMD_BUFFER_SIZE; + pthread_mutex_unlock(&cmdBufferMutex); + return 1; +} + + +/** + * Waits for a certain response type. This method waits for a maximum of + * ms_timeout milliseconds for a specified response command. + *@brief WaitForResponseTimeout + * @param cmd command to wait for + * @param response struct to copy received command into. + * @param ms_timeout + * @return true if command was returned, otherwise false + */ +bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { + + UsbCommand resp; + + if (response == NULL) { + response = &resp; + } + + uint64_t start_time = msclock(); + + // Wait until the command is received + while (true) { + while(getCommand(response)) { + if(response->cmd == cmd){ + return true; + } + } + if (msclock() - start_time > ms_timeout) { + break; + } + if (msclock() - start_time > 2000 && show_warning) { + PrintAndLog("Waiting for a response from the proxmark..."); + PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); + break; + } + } + return false; +} + + +bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { + return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); +} + +bool WaitForResponse(uint32_t cmd, UsbCommand* response) { + return WaitForResponseTimeoutW(cmd, response, -1, true); +} + + //----------------------------------------------------------------------------- // Entry point into our code: called whenever the user types a command and // then presses Enter, which the full command line that they typed. @@ -81,3 +196,38 @@ int CommandReceived(char *Cmd) { return CmdsParse(CommandTable, Cmd); } + +//----------------------------------------------------------------------------- +// Entry point into our code: called whenever we received a packet over USB +// that we weren't necessarily expecting, for example a debug print. +//----------------------------------------------------------------------------- +void UsbCommandReceived(UsbCommand *UC) +{ + switch(UC->cmd) { + // First check if we are handling a debug message + case CMD_DEBUG_PRINT_STRING: { + char s[USB_CMD_DATA_SIZE+1]; + memset(s, 0x00, sizeof(s)); + size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); + memcpy(s,UC->d.asBytes,len); + PrintAndLog("#db# %s", s); + return; + } break; + + case CMD_DEBUG_PRINT_INTEGERS: { + PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); + return; + } break; + + case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { + memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); + return; + } break; + + default: + storeCommand(UC); + break; + } + +} + diff --git a/client/cmdmain.h b/client/cmdmain.h index a833b41..d39bc11 100644 --- a/client/cmdmain.h +++ b/client/cmdmain.h @@ -15,10 +15,13 @@ #include #include "usb_cmd.h" #include "cmdparser.h" -#include "comms.h" - +extern void UsbCommandReceived(UsbCommand *UC); extern int CommandReceived(char *Cmd); +extern bool WaitForResponseTimeoutW(uint32_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); +extern bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout); +extern bool WaitForResponse(uint32_t cmd, UsbCommand* response); +extern void clearCommandBuffer(); extern command_t* getTopLevelCommandTable(); #endif diff --git a/client/cmdparser.c b/client/cmdparser.c index 28ca219..3250899 100644 --- a/client/cmdparser.c +++ b/client/cmdparser.c @@ -14,7 +14,6 @@ #include "ui.h" #include "cmdparser.h" #include "proxmark3.h" -#include "comms.h" void CmdsHelp(const command_t Commands[]) @@ -24,7 +23,7 @@ void CmdsHelp(const command_t Commands[]) int i = 0; while (Commands[i].Name) { - if (!IsOffline() || Commands[i].Offline) + if (!offline || Commands[i].Offline) PrintAndLog("%-16s %s", Commands[i].Name, Commands[i].Help); ++i; } diff --git a/client/comms.c b/client/comms.c deleted file mode 100644 index abbae38..0000000 --- a/client/comms.c +++ /dev/null @@ -1,325 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2009 Michael Gernoth -// Copyright (C) 2010 iZsh -// -// This code is licensed to you under the terms of the GNU GPL, version 2 or, -// at your option, any later version. See the LICENSE.txt file for the text of -// the license. -//----------------------------------------------------------------------------- -// Code for communicating with the proxmark3 hardware. -//----------------------------------------------------------------------------- - -#include - -#include "comms.h" -#include "uart.h" -#include "ui.h" -#include "common.h" -#include "data.h" -#include "util_posix.h" - -// Declare globals. - -// Serial port that we are communicating with the PM3 on. -static serial_port* port; - -// If TRUE, then there is no active connection to the PM3, and we will drop commands sent. -static bool offline; - -// Transmit buffer. -// TODO: Use locks and execute this on the main thread, rather than the receiver -// thread. Running on the main thread means we need to be careful in the -// flasher, as it means SendCommand is no longer async, and can't be used as a -// buffer for a pending command when the connection is re-established. -static UsbCommand txcmd; -static bool txcmd_pending; - -// Used by UsbReceiveCommand as a ring buffer for messages that are yet to be -// processed by a command handler (WaitForResponse{,Timeout}) -static UsbCommand cmdBuffer[CMD_BUFFER_SIZE]; - -// Points to the next empty position to write to -static int cmd_head = 0; - -// Points to the position of the last unread command -static int cmd_tail = 0; - -// to lock cmdBuffer operations from different threads -static pthread_mutex_t cmdBufferMutex = PTHREAD_MUTEX_INITIALIZER; - -// These wrappers are required because it is not possible to access a static -// global variable outside of the context of a single file. - -void SetSerialPort(serial_port* new_port) { - port = new_port; -} - -serial_port* GetSerialPort() { - return port; -} - -void SetOffline(bool new_offline) { - offline = new_offline; -} - -bool IsOffline() { - return offline; -} - -void SendCommand(UsbCommand *c) { - #ifdef COMMS_DEBUG - printf("Sending %04x cmd\n", c->cmd); - #endif - - if (offline) { - PrintAndLog("Sending bytes to proxmark failed - offline"); - return; - } - /** - The while-loop below causes hangups at times, when the pm3 unit is unresponsive - or disconnected. The main console thread is alive, but comm thread just spins here. - Not good.../holiman - **/ - while(txcmd_pending); - txcmd = *c; - txcmd_pending = true; -} - -/** - * @brief This method should be called when sending a new command to the pm3. In case any old - * responses from previous commands are stored in the buffer, a call to this method should clear them. - * A better method could have been to have explicit command-ACKS, so we can know which ACK goes to which - * operation. Right now we'll just have to live with this. - */ -void clearCommandBuffer() -{ - //This is a very simple operation - pthread_mutex_lock(&cmdBufferMutex); - cmd_tail = cmd_head; - pthread_mutex_unlock(&cmdBufferMutex); -} - -/** - * @brief storeCommand stores a USB command in a circular buffer - * @param UC - */ -void storeCommand(UsbCommand *command) -{ - pthread_mutex_lock(&cmdBufferMutex); - if( (cmd_head+1) % CMD_BUFFER_SIZE == cmd_tail) - { - // If these two are equal, we're about to overwrite in the - // circular buffer. - PrintAndLog("WARNING: Command buffer about to overwrite command! This needs to be fixed!"); - } - - // Store the command at the 'head' location - UsbCommand* destination = &cmdBuffer[cmd_head]; - memcpy(destination, command, sizeof(UsbCommand)); - - cmd_head = (cmd_head +1) % CMD_BUFFER_SIZE; //increment head and wrap - pthread_mutex_unlock(&cmdBufferMutex); -} - - -/** - * @brief getCommand gets a command from an internal circular buffer. - * @param response location to write command - * @return 1 if response was returned, 0 if nothing has been received - */ -int getCommand(UsbCommand* response) -{ - pthread_mutex_lock(&cmdBufferMutex); - //If head == tail, there's nothing to read, or if we just got initialized - if (cmd_head == cmd_tail){ - pthread_mutex_unlock(&cmdBufferMutex); - return 0; - } - - //Pick out the next unread command - UsbCommand* last_unread = &cmdBuffer[cmd_tail]; - memcpy(response, last_unread, sizeof(UsbCommand)); - //Increment tail - this is a circular buffer, so modulo buffer size - cmd_tail = (cmd_tail + 1) % CMD_BUFFER_SIZE; - - pthread_mutex_unlock(&cmdBufferMutex); - return 1; -} - -//----------------------------------------------------------------------------- -// Entry point into our code: called whenever we received a packet over USB -// that we weren't necessarily expecting, for example a debug print. -//----------------------------------------------------------------------------- -void UsbCommandReceived(UsbCommand *UC) -{ - switch(UC->cmd) { - - // First check if we are handling a debug message - case CMD_DEBUG_PRINT_STRING: { - char s[USB_CMD_DATA_SIZE+1]; - memset(s, 0x00, sizeof(s)); - size_t len = MIN(UC->arg[0],USB_CMD_DATA_SIZE); - memcpy(s,UC->d.asBytes,len); - PrintAndLog("#db# %s", s); - return; - } - - case CMD_DEBUG_PRINT_INTEGERS: { - PrintAndLog("#db# %08x, %08x, %08x \r\n", UC->arg[0], UC->arg[1], UC->arg[2]); - return; - } - - case CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K: { - // FIXME: This does unsanitised copies into memory when we don't know - // the size of the buffer. - if (sample_buf) { - memcpy(sample_buf+(UC->arg[0]),UC->d.asBytes,UC->arg[1]); - } - return; - } - - default: { - storeCommand(UC); - return; - } - } -} - -// Gets a single command from a proxmark3 device. This should never be used -// with the full client. -// -// @param conn A receiver_arg structure. -// @param command A buffer to store the received command. -bool ReceiveCommand(receiver_arg* conn, UsbCommand* command) { - // Local recieve buffer - size_t rxlen; - byte_t rx[sizeof(UsbCommand)]; - byte_t* prx = rx; - - while (conn->run) { - rxlen = 0; - if (uart_receive(port, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { - prx += rxlen; - if (prx-rx < sizeof(UsbCommand)) { - // Keep reading until we have a completed response. - continue; - } - - // We have a completed response. - memcpy(command, rx, sizeof(UsbCommand)); - return true; - } - - if (prx == rx) { - // We got no complete command while waiting, give up control - return false; - } - } - - // did not get a complete command before being cancelled. - return false; -} - -// Worker thread for processing incoming events from the PM3 -void *uart_receiver(void *targ) { - receiver_arg *conn = (receiver_arg*)targ; - UsbCommand rx; - - while (conn->run) { - #ifdef COMMS_DEBUG - printf("uart_receiver: get lock\n"); - #endif - // Lock up receives, in case they try to take it away from us. - pthread_mutex_lock(&conn->recv_lock); - #ifdef COMMS_DEBUG - printf("uart_receiver: lock acquired\n"); - #endif - - if (port == NULL) { - #ifdef COMMS_DEBUG - printf("uart_receiver: port disappeared\n"); - #endif - // Our port disappeared, stall. This code path matters for the flasher, - // where it is fiddling with the serial port under us. - pthread_mutex_unlock(&conn->recv_lock); - msleep(10); - continue; - } - - bool got_command = ReceiveCommand(conn, &rx); - #ifdef COMMS_DEBUG - printf("uart_receiver: got command\n"); - #endif - pthread_mutex_unlock(&conn->recv_lock); - - if (got_command) { - UsbCommandReceived(&rx); - } - - // We aren't normally trying to transmit in the flasher when the port would - // be reset, so we can just keep going at this point. - if (txcmd_pending) { - if (!uart_send(port, (byte_t*) &txcmd, sizeof(UsbCommand))) { - PrintAndLog("Sending bytes to proxmark failed"); - } - txcmd_pending = false; - } - } - - pthread_exit(NULL); - return NULL; -} - -/** - * Waits for a certain response type. This method waits for a maximum of - * ms_timeout milliseconds for a specified response command. - *@brief WaitForResponseTimeout - * @param cmd command to wait for, or CMD_ANY to take any command. - * @param response struct to copy received command into. - * @param ms_timeout - * @param show_warning - * @return true if command was returned, otherwise false - */ -bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning) { - UsbCommand resp; - - #ifdef COMMS_DEBUG - printf("Waiting for %04x cmd\n", cmd); - #endif - - if (response == NULL) { - response = &resp; - } - - uint64_t start_time = msclock(); - - // Wait until the command is received - for (;;) { - while(getCommand(response)) { - if (cmd == CMD_ANY || response->cmd == cmd) { - return true; - } - } - - if (msclock() - start_time > ms_timeout) { - // We timed out. - break; - } - - if (msclock() - start_time > 2000 && show_warning) { - // 2 seconds elapsed (but this doesn't mean the timeout was exceeded) - PrintAndLog("Waiting for a response from the proxmark..."); - PrintAndLog("Don't forget to cancel its operation first by pressing on the button"); - break; - } - } - return false; -} - -bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout) { - return WaitForResponseTimeoutW(cmd, response, ms_timeout, true); -} - -bool WaitForResponse(uint64_t cmd, UsbCommand* response) { - return WaitForResponseTimeout(cmd, response, -1); -} diff --git a/client/comms.h b/client/comms.h deleted file mode 100644 index 75adeea..0000000 --- a/client/comms.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef COMMS_H_ -#define COMMS_H_ - -#include -#include - -#include "usb_cmd.h" -#include "uart.h" - -#ifndef CMD_BUFFER_SIZE -#define CMD_BUFFER_SIZE 50 -#endif - -#ifndef MAX_DEMOD_BUF_LEN -#define MAX_DEMOD_BUF_LEN (1024*128) -#endif - -#ifndef BIGBUF_SIZE -#define BIGBUF_SIZE 40000 -#endif - -typedef struct { - // If TRUE, continue running the uart_receiver thread. - bool run; - - // Lock around serial port receives - pthread_mutex_t recv_lock; -} receiver_arg; - - -// Wrappers required as static variables can only be used in one file. -void SetSerialPort(serial_port* new_port); -serial_port* GetSerialPort(); -void SetOffline(bool new_offline); -bool IsOffline(); - -void SendCommand(UsbCommand *c); -void *uart_receiver(void *targ); -void UsbCommandReceived(UsbCommand *UC); -void clearCommandBuffer(); -bool WaitForResponseTimeoutW(uint64_t cmd, UsbCommand* response, size_t ms_timeout, bool show_warning); -bool WaitForResponseTimeout(uint64_t cmd, UsbCommand* response, size_t ms_timeout); -bool WaitForResponse(uint64_t cmd, UsbCommand* response); - -#endif // COMMS_H_ diff --git a/client/flash.c b/client/flash.c index bf01b91..7622e8a 100644 --- a/client/flash.c +++ b/client/flash.c @@ -13,7 +13,6 @@ #include #include #include - #include "proxmark3.h" #include "util.h" #include "util_posix.h" @@ -21,7 +20,11 @@ #include "elf.h" #include "proxendian.h" #include "usb_cmd.h" -#include "comms.h" + +void SendCommand(UsbCommand* txcmd); +void ReceiveCommand(UsbCommand* rxcmd); +void CloseProxmark(); +int OpenProxmark(size_t i); // FIXME: what the fuckity fuck unsigned int current_command = CMD_UNKNOWN; @@ -41,32 +44,6 @@ static const uint8_t elf_ident[] = { EV_CURRENT }; -void CloseProxmark(receiver_arg* conn, char* serial_port_name) { - pthread_mutex_lock(&conn->recv_lock); - - // Block the port from being used by anything - serial_port* my_port = GetSerialPort(); - SetSerialPort(NULL); - - // Then close the port. - uart_close(my_port); - pthread_mutex_unlock(&conn->recv_lock); - - // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* - unlink(serial_port_name); -} - -bool OpenProxmark(char* serial_port_name) { - serial_port *new_port = uart_open(serial_port_name); - if (new_port == INVALID_SERIAL_PORT || new_port == CLAIMED_SERIAL_PORT) { - //poll once a second - return false; - } - - SetSerialPort(new_port); - return true; -} - // Turn PHDRs into flasher segments, checking for PHDR sanity and merging adjacent // unaligned segments if needed static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, int num_phdrs) @@ -301,12 +278,9 @@ static int get_proxmark_state(uint32_t *state) { UsbCommand c; c.cmd = CMD_DEVICE_INFO; - SendCommand(&c); + SendCommand(&c); UsbCommand resp; - while (!WaitForResponse(CMD_ANY, &resp)) { - // Keep waiting for a response - msleep(100); - } + ReceiveCommand(&resp); // Three outcomes: // 1. The old bootrom code will ignore CMD_DEVICE_INFO, but respond with an ACK @@ -333,7 +307,7 @@ static int get_proxmark_state(uint32_t *state) } // Enter the bootloader to be able to start flashing -static int enter_bootloader(receiver_arg* conn, char *serial_port_name) +static int enter_bootloader(char *serial_port_name) { uint32_t state; @@ -364,17 +338,16 @@ static int enter_bootloader(receiver_arg* conn, char *serial_port_name) SendCommand(&c); fprintf(stderr,"Press and hold down button NOW if your bootloader requires it.\n"); } - - msleep(100); - CloseProxmark(conn, serial_port_name); + msleep(100); + CloseProxmark(); fprintf(stderr,"Waiting for Proxmark to reappear on %s",serial_port_name); - do { + do { sleep(1); fprintf(stderr, "."); - } while (!OpenProxmark(serial_port_name)); - + } while (!OpenProxmark(0)); fprintf(stderr," Found.\n"); + return 0; } @@ -382,25 +355,23 @@ static int enter_bootloader(receiver_arg* conn, char *serial_port_name) return -1; } -static int wait_for_ack() +static int wait_for_ack(void) { - UsbCommand resp; - while (!WaitForResponse(CMD_ANY, &resp)) { - msleep(100); - } - if (resp.cmd != CMD_ACK) { - printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", resp.cmd); + UsbCommand ack; + ReceiveCommand(&ack); + if (ack.cmd != CMD_ACK) { + printf("Error: Unexpected reply 0x%04" PRIx64 " (expected ACK)\n", ack.cmd); return -1; } return 0; } // Go into flashing mode -int flash_start_flashing(receiver_arg* conn, int enable_bl_writes,char *serial_port_name) +int flash_start_flashing(int enable_bl_writes,char *serial_port_name) { uint32_t state; - if (enter_bootloader(conn, serial_port_name) < 0) + if (enter_bootloader(serial_port_name) < 0) return -1; if (get_proxmark_state(&state) < 0) @@ -499,9 +470,9 @@ void flash_free(flash_file_t *ctx) } // just reset the unit -int flash_stop_flashing() { +int flash_stop_flashing(void) { UsbCommand c = {CMD_HARDWARE_RESET}; - SendCommand(&c); - msleep(100); - return 0; + SendCommand(&c); + msleep(100); + return 0; } diff --git a/client/flash.h b/client/flash.h index 279062a..3e9f77a 100644 --- a/client/flash.h +++ b/client/flash.h @@ -11,7 +11,6 @@ #include #include "elf.h" -#include "comms.h" typedef struct { void *data; @@ -27,12 +26,10 @@ typedef struct { } flash_file_t; int flash_load(flash_file_t *ctx, const char *name, int can_write_bl); -int flash_start_flashing(receiver_arg* conn, int enable_bl_writes, char *serial_port_name); +int flash_start_flashing(int enable_bl_writes,char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); -void CloseProxmark(receiver_arg* conn, char* serial_port_name); -bool OpenProxmark(char* serial_port_name); #endif diff --git a/client/flasher.c b/client/flasher.c index 40e4052..f257d99 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -10,15 +10,12 @@ #include #include #include -#include - #include "proxmark3.h" #include "util.h" #include "util_posix.h" #include "flash.h" #include "uart.h" #include "usb_cmd.h" -#include "comms.h" #ifdef _WIN32 # define unlink(x) @@ -26,6 +23,9 @@ # include #endif +static serial_port sp; +static char* serial_port_name; + void cmd_debug(UsbCommand* UC) { // Debug printf("UsbCommand length[len=%zd]\n",sizeof(UsbCommand)); @@ -40,6 +40,45 @@ void cmd_debug(UsbCommand* UC) { printf("...\n"); } +void SendCommand(UsbCommand* txcmd) { +// printf("send: "); +// cmd_debug(txcmd); + if (!uart_send(sp,(byte_t*)txcmd,sizeof(UsbCommand))) { + printf("Sending bytes to proxmark failed\n"); + exit(1); + } +} + +void ReceiveCommand(UsbCommand* rxcmd) { + byte_t* prxcmd = (byte_t*)rxcmd; + byte_t* prx = prxcmd; + size_t rxlen; + while (true) { + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-prxcmd), &rxlen)) { + prx += rxlen; + if ((prx-prxcmd) >= sizeof(UsbCommand)) { + return; + } + } + } +} + +void CloseProxmark() { + // Clean up the port + uart_close(sp); + // Fix for linux, it seems that it is extremely slow to release the serial port file descriptor /dev/* + unlink(serial_port_name); +} + +int OpenProxmark(size_t i) { + sp = uart_open(serial_port_name); + if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { + //poll once a second + return 0; + } + return 1; +} + static void usage(char *argv0) { fprintf(stderr, "Usage: %s [-b] image.elf [image.elf...]\n\n", argv0); @@ -47,10 +86,9 @@ static void usage(char *argv0) //Is the example below really true? /Martin fprintf(stderr, "Example:\n\n\t %s path/to/osimage.elf path/to/fpgaimage.elf\n", argv0); fprintf(stderr, "\nExample (Linux):\n\n\t %s /dev/ttyACM0 armsrc/obj/fullimage.elf\n", argv0); - fprintf(stderr, "\nNote (Linux): if the flasher gets stuck at 'Waiting for Proxmark to reappear',\n"); - fprintf(stderr, " you may need to blacklist proxmark for modem-manager. v1.4.14 and later\n"); - fprintf(stderr, " include this configuration patch already. The change can be found at:\n"); - fprintf(stderr, " https://cgit.freedesktop.org/ModemManager/ModemManager/commit/?id=6e7ff47\n\n"); + fprintf(stderr, "\nNote (Linux): if the flasher gets stuck in 'Waiting for Proxmark to reappear on ',\n"); + fprintf(stderr, " you need to blacklist proxmark for modem-manager - see wiki for more details:\n"); + fprintf(stderr, " http://code.google.com/p/proxmark3/wiki/Linux\n\n"); } #define MAX_FILES 4 @@ -61,10 +99,7 @@ int main(int argc, char **argv) int num_files = 0; int res; flash_file_t files[MAX_FILES]; - receiver_arg conn; - pthread_t reader_thread; - memset(&conn, 0, sizeof(receiver_arg)); memset(files, 0, sizeof(files)); if (argc < 3) { @@ -91,22 +126,16 @@ int main(int argc, char **argv) } } - pthread_mutex_init(&conn.recv_lock, NULL); + serial_port_name = argv[1]; + + fprintf(stderr,"Waiting for Proxmark to appear on %s",serial_port_name); + do { + msleep(1000); + fprintf(stderr, "."); + } while (!OpenProxmark(0)); + fprintf(stderr," Found.\n"); - char* serial_port_name = argv[1]; - - fprintf(stderr,"Waiting for Proxmark to appear on %s", serial_port_name); - do { - sleep(1); - fprintf(stderr, "."); - } while (!OpenProxmark(serial_port_name)); - fprintf(stderr," Found.\n"); - - // Lets start up the communications thread - conn.run = true; - pthread_create(&reader_thread, NULL, &uart_receiver, &conn); - - res = flash_start_flashing(&conn, can_write_bl, serial_port_name); + res = flash_start_flashing(can_write_bl,serial_port_name); if (res < 0) return -1; @@ -126,11 +155,7 @@ int main(int argc, char **argv) if (res < 0) return -1; - // Stop the command thread. - conn.run = false; - pthread_join(reader_thread, NULL); - CloseProxmark(&conn, serial_port_name); - pthread_mutex_destroy(&conn.recv_lock); + CloseProxmark(); fprintf(stderr, "All done.\n\n"); fprintf(stderr, "Have a nice day!\n"); diff --git a/client/proxgui.cpp b/client/proxgui.cpp index 6e4ec98..e899174 100644 --- a/client/proxgui.cpp +++ b/client/proxgui.cpp @@ -11,15 +11,11 @@ #include "proxgui.h" #include "proxguiqt.h" #include "proxmark3.h" -#include "uart.h" static ProxGuiQT *gui = NULL; static WorkerThread *main_loop_thread = NULL; -WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, - bool usb_present, serial_port* sp) - : script_cmds_file(script_cmds_file), script_cmd(script_cmd), - usb_present(usb_present), sp(sp) +WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool usb_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), usb_present(usb_present) { } @@ -28,7 +24,7 @@ WorkerThread::~WorkerThread() } void WorkerThread::run() { - main_loop(script_cmds_file, script_cmd, usb_present, sp); + main_loop(script_cmds_file, script_cmd, usb_present); } extern "C" void ShowGraphWindow(void) @@ -63,9 +59,7 @@ extern "C" void MainGraphics(void) gui->MainLoop(); } -extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, - char *script_cmd, bool usb_present, - serial_port* sp) +extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present) { #ifdef Q_WS_X11 bool useGUI = getenv("DISPLAY") != 0; @@ -75,7 +69,7 @@ extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, if (!useGUI) return; - main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present, sp); + main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, usb_present); gui = new ProxGuiQT(argc, argv, main_loop_thread); } diff --git a/client/proxgui.h b/client/proxgui.h index ea3abf3..77bcbf0 100644 --- a/client/proxgui.h +++ b/client/proxgui.h @@ -14,13 +14,12 @@ extern "C" { #include #include -#include "uart.h" void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); void MainGraphics(void); -void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); +void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool usb_present); void ExitGraphics(void); #define MAX_GRAPH_TRACE_LEN (40000*8) @@ -31,6 +30,7 @@ extern int s_Buff[MAX_GRAPH_TRACE_LEN]; extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; extern int CommandFinished; +extern int offline; extern bool GridLocked; //Operations defined in data_operations diff --git a/client/proxguiqt.h b/client/proxguiqt.h index 4b2b2a2..45a65b0 100644 --- a/client/proxguiqt.h +++ b/client/proxguiqt.h @@ -21,7 +21,6 @@ #include #include -#include "uart.h" #include "ui/ui_overlays.h" /** * @brief The actual plot, black area were we paint the graph @@ -92,14 +91,13 @@ class ProxWidget : public QWidget class WorkerThread : public QThread { Q_OBJECT; public: - WorkerThread(char*, char*, bool, serial_port*); + WorkerThread(char*, char*, bool); ~WorkerThread(); void run(); private: char *script_cmds_file = NULL; char *script_cmd = NULL; bool usb_present; - serial_port *sp = NULL; }; class ProxGuiQT : public QObject diff --git a/client/proxmark3.c b/client/proxmark3.c index 0acf34f..99ba9fb 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -27,42 +27,86 @@ #include "cmdparser.h" #include "cmdhw.h" #include "whereami.h" -#include "comms.h" #ifdef _WIN32 #define SERIAL_PORT_H "com3" -#elif __APPLE__ -#define SERIAL_PORT_H "/dev/tty.usbmodem*" #else #define SERIAL_PORT_H "/dev/ttyACM0" #endif -void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, serial_port* sp) { - receiver_arg conn; +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; + +static serial_port sp; +static UsbCommand txcmd; +volatile static bool txcmd_pending = false; + +void SendCommand(UsbCommand *c) { + #if 0 + printf("Sending %d bytes\n", sizeof(UsbCommand)); + #endif + + if (offline) { + PrintAndLog("Sending bytes to proxmark failed - offline"); + return; + } + /** + The while-loop below causes hangups at times, when the pm3 unit is unresponsive + or disconnected. The main console thread is alive, but comm thread just spins here. + Not good.../holiman + **/ + while(txcmd_pending); + txcmd = *c; + txcmd_pending = true; +} + +struct receiver_arg { + int run; +}; + +byte_t rx[sizeof(UsbCommand)]; +byte_t* prx = rx; + +static void *uart_receiver(void *targ) { + struct receiver_arg *arg = (struct receiver_arg*)targ; + size_t rxlen; + + while (arg->run) { + rxlen = 0; + if (uart_receive(sp, prx, sizeof(UsbCommand) - (prx-rx), &rxlen) && rxlen) { + prx += rxlen; + if (prx-rx < sizeof(UsbCommand)) { + continue; + } + UsbCommandReceived((UsbCommand*)rx); + } + prx = rx; + + if(txcmd_pending) { + if (!uart_send(sp, (byte_t*) &txcmd, sizeof(UsbCommand))) { + PrintAndLog("Sending bytes to proxmark failed"); + } + txcmd_pending = false; + } + } + + pthread_exit(NULL); + return NULL; +} + + +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present) { + struct receiver_arg rarg; char *cmd = NULL; pthread_t reader_thread; bool execCommand = (script_cmd != NULL); bool stdinOnPipe = !isatty(STDIN_FILENO); - - memset(&conn, 0, sizeof(receiver_arg)); - pthread_mutex_init(&conn.recv_lock, NULL); - - - // TODO: Move this into comms.c - PlotGridXdefault = 64; - PlotGridYdefault = 64; - showDemod = true; - CursorScaleFactor = 1; - + if (usb_present) { - conn.run = true; - SetSerialPort(sp); - SetOffline(false); - pthread_create(&reader_thread, NULL, &uart_receiver, &conn); + rarg.run = 1; + pthread_create(&reader_thread, NULL, &uart_receiver, &rarg); // cache Version information now: CmdVersion(NULL); - } else { - SetOffline(true); } // file with script @@ -78,7 +122,7 @@ void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, seria read_history(".history"); - while (1) { + while(1) { // If there is a script file if (script_file) { @@ -150,7 +194,7 @@ void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, seria write_history(".history"); if (usb_present) { - conn.run = false; + rarg.run = 0; pthread_join(reader_thread, NULL); } @@ -158,8 +202,6 @@ void main_loop(char *script_cmds_file, char* script_cmd, bool usb_present, seria fclose(script_file); script_file = NULL; } - - pthread_mutex_destroy(&conn.recv_lock); } static void dumpAllHelp(int markdown) @@ -232,8 +274,6 @@ int main(int argc, char* argv[]) { bool addLuaExec = false; char *script_cmds_file = NULL; char *script_cmd = NULL; - serial_port *sp = NULL; - g_debugMode = 0; if (argc < 2) { show_help(true, argv[0]); @@ -330,11 +370,14 @@ int main(int argc, char* argv[]) { if (sp == INVALID_SERIAL_PORT) { printf("ERROR: invalid serial port\n"); usb_present = false; + offline = 1; } else if (sp == CLAIMED_SERIAL_PORT) { printf("ERROR: serial port is claimed by another process\n"); usb_present = false; + offline = 1; } else { usb_present = true; + offline = 0; } // create a mutex to avoid interlacing print commands from our different threads @@ -342,23 +385,23 @@ int main(int argc, char* argv[]) { #ifdef HAVE_GUI #ifdef _WIN32 - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); #else char* display = getenv("DISPLAY"); if (display && strlen(display) > 1) { - InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present, sp); + InitGraphics(argc, argv, script_cmds_file, script_cmd, usb_present); MainGraphics(); } else { - main_loop(script_cmds_file, script_cmd, usb_present, sp); + main_loop(script_cmds_file, script_cmd, usb_present); } #endif #else - main_loop(script_cmds_file, script_cmd, usb_present, sp); + main_loop(script_cmds_file, script_cmd, usb_present); #endif // Clean up the port diff --git a/client/proxmark3.h b/client/proxmark3.h index 11b8aab..c6185c4 100644 --- a/client/proxmark3.h +++ b/client/proxmark3.h @@ -13,7 +13,6 @@ #define PROXMARK3_H__ #include "usb_cmd.h" -#include "uart.h" #define PROXPROMPT "proxmark3> " @@ -24,7 +23,7 @@ extern "C" { void SendCommand(UsbCommand *c); const char *get_my_executable_path(void); const char *get_my_executable_directory(void); -void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present, serial_port* sp); +void main_loop(char *script_cmds_file, char *script_cmd, bool usb_present); #ifdef __cplusplus } diff --git a/client/ui.c b/client/ui.c index 48869fd..df2c3ce 100644 --- a/client/ui.c +++ b/client/ui.c @@ -21,7 +21,7 @@ double CursorScaleFactor = 1; int PlotGridX=0, PlotGridY=0, PlotGridXdefault= 64, PlotGridYdefault= 64, CursorCPos= 0, CursorDPos= 0; int offline; -bool flushAfterWrite = false; //buzzy +int flushAfterWrite = 0; //buzzy int GridOffset = 0; bool GridLocked = false; bool showDemod = true; @@ -62,6 +62,7 @@ void PrintAndLog(char *fmt, ...) } #else // We are using libedit (OSX), which doesn't support this flag. + int need_hack = 0; #endif va_start(argptr, fmt); @@ -71,9 +72,6 @@ void PrintAndLog(char *fmt, ...) va_end(argptr); printf("\n"); - // This needs to be wrapped in ifdefs, as this if optimisation is disabled, - // this block won't be removed, and it'll fail at the linker. -#ifdef RL_STATE_READCMD if (need_hack) { rl_restore_prompt(); rl_replace_line(saved_line, 0); @@ -81,7 +79,6 @@ void PrintAndLog(char *fmt, ...) rl_redisplay(); free(saved_line); } -#endif if (logging && logfile) { vfprintf(logfile, fmt, argptr2); @@ -103,8 +100,3 @@ void SetLogFilename(char *fn) { logfilename = fn; } - -void SetFlushAfterWrite(bool flush_after_write) { - flushAfterWrite = flush_after_write; -} - diff --git a/client/ui.h b/client/ui.h index 2986efa..4049033 100644 --- a/client/ui.h +++ b/client/ui.h @@ -13,11 +13,6 @@ #include #include -#include - -// a global mutex to prevent interlaced printing from different threads -pthread_mutex_t print_lock; -extern uint8_t g_debugMode; void ShowGui(void); void HideGraphWindow(void); @@ -28,7 +23,8 @@ void SetLogFilename(char *fn); extern double CursorScaleFactor; extern int PlotGridX, PlotGridY, PlotGridXdefault, PlotGridYdefault, CursorCPos, CursorDPos, GridOffset; -extern bool flushAfterWrite; //buzzy +extern int offline; +extern int flushAfterWrite; //buzzy extern bool GridLocked; extern bool showDemod; diff --git a/common/iso15693tools.c b/common/iso15693tools.c index cf7d534..26e636c 100644 --- a/common/iso15693tools.c +++ b/common/iso15693tools.c @@ -50,9 +50,8 @@ int Iso15693AddCrc(uint8_t *req, int n) { return n+2; } -#ifdef ON_DEVICE + int sprintf(char *str, const char *format, ...); -#endif // returns a string representation of the UID // UID is transmitted and stored LSB first, displayed MSB first diff --git a/include/usb_cmd.h b/include/usb_cmd.h index df2923a..194a9d5 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -212,7 +212,7 @@ typedef struct{ #define CMD_HF_SNIFFER 0x0800 #define CMD_UNKNOWN 0xFFFF -#define CMD_ANY 0xFFFFFFFFFFFFFFFF + //Mifare simulation flags #define FLAG_INTERACTIVE 0x01 From fefb7f5376bb62901aac7494d42fb78e5ea5983a Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Fri, 27 Oct 2017 17:58:24 +0300 Subject: [PATCH 13/19] appveyor.yml. display return values (#451) * display return values from commands and compare them with value. as promised in #440 --- appveyor.yml | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c8e58b5..eb22114 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -175,7 +175,7 @@ test_script: $global:TestsPassed=$true - Function ExecTest($Name, $File, $Cmd) { + Function ExecTest($Name, $File, $Cmd, $CheckResult) { #--- begin Job @@ -191,6 +191,21 @@ test_script: $Cond=&$sb if ($Cond -eq $null){ + } ElseIf($using:CheckResult -ne $null) { + [String]$searchstr="" + if ($Cond -is [Object]){ + ForEach($line in $Cond){ + Write-host $line -ForegroundColor Gray + $searchstr += $line + } + }else{ + Write-host "$Cond" -ForegroundColor Gray + $searchstr = $Cond + } + If($searchstr -like "*$using:CheckResult*") { + $res=$true + } + $Cond="*$using:CheckResult*" } Else { If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ if ($Cond -is "String" -and $Cond -like "*passed*"){ @@ -258,9 +273,9 @@ test_script: ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} - ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf' | grep -q at_enc && echo Passed || echo Failed"} + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf'"} "at_enc" - ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000' | grep -q 'found:' && echo Passed || echo Failed"} + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" if ($global:TestsPassed) { From 618c220c383db2b9cde32261e90a008b0fd2b736 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Sun, 29 Oct 2017 17:34:51 +0200 Subject: [PATCH 14/19] small fixes in ReaderIso14443a() (#446) --- armsrc/iso14443a.c | 12 ++++++++---- client/cmdhf14a.c | 13 +++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 026b177..06a83fe 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1698,6 +1698,13 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u int cascade_level = 0; int len; + // init card struct + if(p_hi14a_card) { + p_hi14a_card->uidlen = 0; + memset(p_hi14a_card->uid, 0, 10); + p_hi14a_card->ats_len = 0; + } + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field ReaderTransmitBitsPar(wupa, 7, NULL, NULL); @@ -1706,8 +1713,6 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u if(p_hi14a_card) { memcpy(p_hi14a_card->atqa, resp, 2); - p_hi14a_card->uidlen = 0; - memset(p_hi14a_card->uid,0,10); } if (anticollision) { @@ -1813,7 +1818,6 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u if(p_hi14a_card) { p_hi14a_card->sak = sak; - p_hi14a_card->ats_len = 0; } // non iso14443a compliant tag @@ -1908,7 +1912,7 @@ void ReaderIso14443a(UsbCommand *c) size_t lenbits = c->arg[1] >> 16; uint32_t timeout = c->arg[2]; uint32_t arg0 = 0; - byte_t buf[USB_CMD_DATA_SIZE]; + byte_t buf[USB_CMD_DATA_SIZE] = {0}; uint8_t par[MAX_PARITY_SIZE]; if(param & ISO14A_CONNECT) { diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index e3f1a5f..09dcd34 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -732,8 +732,17 @@ static void waitCmd(uint8_t iSelect) if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { recv = resp.d.asBytes; - uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; - PrintAndLog("received %i octets", iLen); + uint8_t iLen = resp.arg[0]; + if (iSelect){ + iLen = resp.arg[1]; + if (iLen){ + PrintAndLog("Card selected. UID[%i]:", iLen); + } else { + PrintAndLog("Can't select card."); + } + } else { + PrintAndLog("received %i bytes:", iLen); + } if(!iLen) return; hexout = (char *)malloc(iLen * 3 + 1); From 7dac1034e5db678e0b8a291b58d6f2eb2fdf07df Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 09:51:05 +0200 Subject: [PATCH 15/19] Add hf mf info, change hf mf reader (#452) * copy functionality from `hf 14a reader` to `hf 14a info` * added command `hf 14a reader` with simple anticollision-select procedure. * add parameters to `hf 14a reader`. may start and end acting as reader --- client/cmdhf14a.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 09dcd34..3f103f5 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -133,7 +133,80 @@ int CmdHF14AList(const char *Cmd) return 0; } -int CmdHF14AReader(const char *Cmd) +int CmdHF14AReader(const char *Cmd) { + uint32_t cm = ISO14A_CONNECT; + bool disconnectAfter = false; + + int cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + PrintAndLog("Usage: hf 14a reader [d] [3]"); + PrintAndLog(" d drop the signal field after command executed"); + PrintAndLog(" x just drop the signal field"); + PrintAndLog(" 3 ISO14443-3 select only (skip RATS)"); + return 0; + case '3': + cm |= ISO14A_NO_RATS; + break; + case 'd': + case 'D': + disconnectAfter = true; + break; + case 'x': + case 'X': + disconnectAfter = true; + cm = cm - ISO14A_CONNECT; + break; + default: + PrintAndLog("Unknown command."); + return 1; + } + + cmdp++; + } + + if (!disconnectAfter) + cm |= ISO14A_NO_DISCONNECT; + + UsbCommand c = {CMD_READER_ISO_14443a, {cm, 0, 0}}; + SendCommand(&c); + + if (ISO14A_CONNECT & cm) { + UsbCommand resp; + WaitForResponse(CMD_ACK,&resp); + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision + + if(select_status == 0) { + PrintAndLog("iso14443a card select failed"); + return 1; + } + + if(select_status == 3) { + PrintAndLog("Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + return 1; + } + + PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); + } + PrintAndLog("Card is selected. You can now start sending commands"); + } else { + PrintAndLog("Field dropped."); + } + return 0; +} + +int CmdHF14AInfo(const char *Cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; SendCommand(&c); @@ -764,7 +837,8 @@ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"list", CmdHF14AList, 0, "[Deprecated] List ISO 14443a history"}, - {"reader", CmdHF14AReader, 0, "Act like an ISO14443 Type A reader"}, + {"reader", CmdHF14AReader, 0, "Start acting like an ISO14443 Type A reader"}, + {"info", CmdHF14AInfo, 0, "Reads card and shows information about it"}, {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443a tag"}, {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, From fe842bed9ce5d92a59605c239bec71f19c94d31a Mon Sep 17 00:00:00 2001 From: merlokk Date: Wed, 1 Nov 2017 13:54:03 +0200 Subject: [PATCH 16/19] fixed bug with `hf search 14a` --- client/cmdhf.c | 2 +- client/cmdhf14a.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 6aa5ae4..168296b 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -663,7 +663,7 @@ int CmdHFList(const char *Cmd) int CmdHFSearch(const char *Cmd){ int ans = 0; PrintAndLog(""); - ans = CmdHF14AReader("s"); + ans = CmdHF14AInfo("s"); if (ans > 0) { PrintAndLog("\nValid ISO14443A Tag Found - Quiting Search\n"); return ans; diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index dfdf1f4..2556678 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -18,6 +18,7 @@ int CmdHF14A(const char *Cmd); int CmdHF14AList(const char *Cmd); int CmdHF14AMifare(const char *Cmd); int CmdHF14AReader(const char *Cmd); +extern int CmdHF14AInfo(const char *Cmd); int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); char* getTagInfo(uint8_t uid); From 83df98d691c002d99d5a8f37d17f9205ba649cc2 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 18:44:43 +0200 Subject: [PATCH 17/19] change comment in iso14443a.c (#459) --- armsrc/iso14443a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 06a83fe..987d29a 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1820,7 +1820,7 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u p_hi14a_card->sak = sak; } - // non iso14443a compliant tag + // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) if( (sak & 0x20) == 0) return 2; if (!no_rats) { From fe6bf3c58c939bfa076f06034e7963ec27080ef0 Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 18:48:59 +0200 Subject: [PATCH 18/19] improve `hf 14a info` (#457) * added validate_prng_nonce from iceman1001 fork * added DetectClassicPrng from iceman1001 fork --- client/cmdhf14a.c | 15 +++++++++ client/mifarehost.c | 69 ++++++++++++++++++++++++++++++++++++++++ client/mifarehost.h | 1 + common/crapto1/crapto1.c | 1 - 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 3f103f5..928864a 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -244,8 +244,10 @@ int CmdHF14AInfo(const char *Cmd) PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + bool isMifareClassic = true; switch (card.sak) { case 0x00: + isMifareClassic = false; //***************************************test**************** // disconnect @@ -480,6 +482,19 @@ int CmdHF14AInfo(const char *Cmd) // try to see if card responses to "chinese magic backdoor" commands. mfCIdentify(); + if (isMifareClassic) { + switch(DetectClassicPrng()) { + case 0: + PrintAndLog("Prng detection: HARDEND (hardnested)"); + break; + case 1: + PrintAndLog("Prng detection: WEAK"); + break; + default: + PrintAndLog("Prng detection error."); + } + } + return select_status; } diff --git a/client/mifarehost.c b/client/mifarehost.c index fe1a8ed..67277b5 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -903,3 +903,72 @@ int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, return 0; } +/** validate_prng_nonce + * Determine if nonce is deterministic. ie: Suspectable to Darkside attack. + * returns + * true = weak prng + * false = hardend prng + */ +bool validate_prng_nonce(uint32_t nonce) { + uint16_t *dist = 0; + uint16_t x, i; + + dist = malloc(2 << 16); + if(!dist) + return -1; + + // init prng table: + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + + uint32_t res = (65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535; + + free(dist); + return (res == 16); +} + +/* Detect Tag Prng, +* function performs a partial AUTH, where it tries to authenticate against block0, key A, but only collects tag nonce. +* the tag nonce is check to see if it has a predictable PRNG. +* @returns +* TRUE if tag uses WEAK prng (ie Now the NACK bug also needs to be present for Darkside attack) +* FALSE is tag uses HARDEND prng (ie hardnested attack possible, with known key) +*/ +int DetectClassicPrng(void){ + + UsbCommand resp, respA; + uint8_t cmd[] = {0x60, 0x00}; // MIFARE_AUTH_KEYA + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + + UsbCommand c = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; + memcpy(c.d.asBytes, cmd, sizeof(cmd)); + + clearCommandBuffer(); + SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLog("PRNG UID: Reply timeout."); + return -1; + } + + // if select tag failed. + if (resp.arg[0] == 0) { + PrintAndLog("PRNG error: selecting tag failed, can't detect prng."); + return -1; + } + + if (!WaitForResponseTimeout(CMD_ACK, &respA, 5000)) { + PrintAndLog("PRNG data: Reply timeout."); + return -1; + } + + // check respA + if (respA.arg[0] != 4) { + PrintAndLog("PRNG data error: Wrong length: %d", respA.arg[0]); + return -1; + } + + uint32_t nonce = bytes_to_num(respA.d.asBytes, respA.arg[0]); + return validate_prng_nonce(nonce); +} diff --git a/client/mifarehost.h b/client/mifarehost.h index 118d55c..031dac1 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -60,5 +60,6 @@ extern int saveTraceCard(void); extern int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); extern int mfCIdentify(); +extern int DetectClassicPrng(void); #endif diff --git a/common/crapto1/crapto1.c b/common/crapto1/crapto1.c index 9398a1f..1edfca1 100644 --- a/common/crapto1/crapto1.c +++ b/common/crapto1/crapto1.c @@ -426,7 +426,6 @@ int nonce_distance(uint32_t from, uint32_t to) return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; } - static uint32_t fastfwd[2][8] = { { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; From 75e42ef5dfc6868e696cbc13ef373d97a3040ada Mon Sep 17 00:00:00 2001 From: Oleg Moiseenko Date: Wed, 1 Nov 2017 21:37:18 +0200 Subject: [PATCH 19/19] added changelog (#460) --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 044538c..60caf41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - `hf mf nested` Check keys after they have found (Merlok) - `hf mf chk` Move main cycle to arm (Merlok) - Changed proxmark command line parameter `flush` to `-f` or `-flush` (Merlok) +- Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) @@ -32,6 +33,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added to proxmark command line parameters `w` - wait 20s for serial port (Merlok) - Added to proxmark command line parameters `c` and `l` - execute command and lua script from command line (Merlok) - Added to proxmark ability to execute commands from stdin (pipe) (Merlok) +- Added `hf 14a info` and moved there functionality from `hf 14a reader` (Merlok) +- Added to `hf 14a info` detection of weak prng from Iceman1001 fork (Merlok) ## [3.0.1][2017-06-08]