Code: Select all
put
index: 3
Float
5759439474786304.0
5759438937915392.0
0x14762E00000000
0x14762DE0000000
Code: Select all
put
index: 3
Float
5759439474786304.0
5759438937915392.0
0x14762E00000000
0x14762DE0000000
It is not the correct result for recreating numput.the tests show the expected accuracy
There is hardly any advanced math going on here. The bit manipulations are basic. Your approach requires knowledge about how numbers are stored, so this is not the way I expected the puzzle to be solved. The expected solution would demonstrate a knowledge about the AHK language, not about number formats. Your approach is acceptable, but currently not correctly implemented. I will give you a point for a good effort and good partial solution . I will give one point to whoever completes jeeswg's solution. I will give two points to whoever completes the full puzzle.we're talking maths degree level stuff here
Code: Select all
;slightly improved PuzPut function which handles rounding
;the MsgBox indicating a slight discrepancy now appears less frequently
;before: e.g. 2381/10000 = 0.2381 [24%]
;after: e.g. 67/10000 = 0.0067 [0.7%]
;AHK v2 script
;'PuzGet'/'PuzPut' - recreate NumGet/NumPut functionality
;where 'Puz' refers to 'Code Puzzle Thread'
;note: when converting from/to Floats,
;the fractions could be *fractionally* different,
;although the tests show the expected accuracy
;note: the maths of handling the raw binary data
;of the Floats is very fiddly, so errors are possible
;AHK v2 is used because you can convert from/to Doubles
;by using the address of a variable
w:: ;test PuzPut and PuzGet functions
VarSetCapacity(vData1, 8, 0)
VarSetCapacity(vData2, 8, 0)
NumPut(-12345678, &vData1, "Int")
PuzPut(-12345678, &vData2, "Int")
MsgBox(NumGet(&vData1, "Int"))
MsgBox(NumGet(&vData2, "Int"))
MsgBox(PuzGet(&vData1, "Int"))
return
q:: ;test PuzPut and PuzGet functions
;oType := StrSplit("Char,Short,Int,Int64,Ptr,UChar,UShort,UInt,UInt64,UPtr", ",")
;oType := StrSplit("Char,Short,Int,Int64,Ptr,UChar,UShort,UInt,UInt64,UPtr,Float,Double", ",")
oType := StrSplit("Float,Double", ",")
VarSetCapacity(vData1, 8)
VarSetCapacity(vData2, 8)
vCount := 0
Loop 10000
{
try
{
vNum := Random(-2147483648, 2147483647)
vNum *= 256**3
vTemp := Random(1, oType.Length())
vType := oType[vTemp]
NumPut(vNum, &vData1, vType)
PuzPut(vNum, &vData2, vType)
vNum1 := NumGet(&vData1, vType)
vNum2 := NumGet(&vData2, vType)
vNum1X := PuzGet(&vData1, vType)
vHex1 := Format("0x{:X}", vNum1)
vHex2 := Format("0x{:X}", vNum2)
vHex1X := Format("0x{:X}", vNum1X)
}
catch
continue
;vCount++
vIsErr := 0
;if 0
if !(vNum1 = vNum2)
;if !InStr(vType, "Int64")
MsgBox("put`r`n" "index: " A_Index "`r`n" vType "`r`n" vNum1 "`r`n" vNum2 "`r`n" vHex1 "`r`n" vHex2)
, vIsErr := 1
;if 0
if !(vNum1 = vNum1X)
MsgBox("get`r`n" "index: " A_Index "`r`n" vType "`r`n" vNum1 "`r`n" vNum1X "`r`n" vHex1 "`r`n" vHex1X)
, vIsErr := 1
if vIsErr
vCount++
}
MsgBox("done " vCount)
return
;==================================================
PuzGet(vAddr, vType:="UPtr")
{
local
static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
static vIsReady := 0
if !vIsReady
{
oChar := {0:0}
VarSetCapacity(vData, 1, 0)
Loop 255
PuzPut(A_Index, &vData, "UChar")
, vOrd := Ord(StrGet(&vData, 1, "CP0"))
, oChar[vOrd] := A_Index
vIsReady := 1
}
if (vType = "Double")
{
vData := 0.0
memcpy(&vData, vAddr, 8)
return vData
}
if (vType = "Float")
{
vNum := PuzGet(vAddr+0, "UInt")
vSign := !!(vNum & 0x80000000)
vPow := ((vNum & 0x7F800000) >> 23)
vNum := vNum & 0x7FFFFF
if (vPow = 255)
vPow := 2047
else if (vPow > 0)
vPow := (vPow - 127 + 1023)
;else if (vPow = 0)
; vPow := 0
vNum <<= (52-23)
vTemp := (vSign ? -0x8000000000000000 : 0) | (vPow << 52) | vNum
VarSetCapacity(vData, 8)
PuzPut(vTemp, &vData, "Int64")
return PuzGet(&vData, "Double")
}
vIsSigned := !RegExMatch(vType, "i)^U")
vSize := oType[RegExReplace(vType, "i)^U")]
vNum := 0
Loop vSize
vNum |= oChar[Ord(StrGet(vAddr+A_Index-1, 1, "CP0"))] << (A_Index*8-8)
if vIsSigned
if (vSize = 1) && (vNum >= 128)
return vNum - 256
else if (vSize = 2) && (vNum >= 32768)
return vNum - 65536
else if (vSize = 4) && (vNum >= 2147483648)
return vNum - 4294967296
return vNum
}
PuzPut(vNum, vAddr, vType:="UPtr")
{
local
static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
static vIsReady := 0, vDataNull := 0
if !vIsReady
{
;oChar := {0:""}
oChar := {}
VarSetCapacity(vDataNull, 1, 0)
Loop 255
oChar[A_Index] := Chr(A_Index)
vIsReady := 1
}
if (vType = "Double")
{
vData := vNum + 0.0
memcpy(vAddr, &vData, 8)
return
}
if (vType = "Float")
{
vData := vNum + 0.0
vNum := PuzGet(&vData, "Int64")
;0x8000000000000000 (UInt64) = 0x8000000000000000 - 0x10000000000000000 (Int64) = -0x8000000000000000 (Int64)
vSign := !!(vNum & -0x8000000000000000)
vPow := ((vNum & 0x7FF0000000000000) >> 52)
vNum := vNum & 0xFFFFFFFFFFFFF
if (vPow = 2047)
vPow := 255
else if (vPow > 0)
vPow := (vPow - 1023 + 127) & 0xFF
;else if (vPow = 0)
; vPow := 0
vBit := (vNum >> (52-24)) & 0x1 ;truncate number, but if bit is 1, round up
vNum >>= (52-23)
;MsgBox vBit
;if (vBit)
;throw
;handle rounding:
;if 0
if vBit
{
vTemp := 1
Loop 22
{
if !(vNum & vTemp)
{
vNum |= vTemp
vNum ^= vTemp-1
break
}
vTemp <<= 1
}
if (vTemp = 2**22)
vNum := 0, vPow += 1
}
vTemp := (vSign << 31) | (vPow << 23) | vNum
PuzPut(vTemp, vAddr+0, "UInt")
return
}
vSize := oType[RegExReplace(vType, "i)^U")]
Loop Min(vSize, 7)
vTemp := vNum & (0xFF << (A_Index*8-8))
, vTemp >>= (A_Index*8-8)
, (vTemp=0) ? memcpy(vAddr+A_Index-1, &vDataNull, 1) : memcpy(vAddr+A_Index-1, oChar.GetAddress(vTemp), 1)
;0xFF00000000000000 (UInt64) = (0xFF00000000000000 - 0x10000000000000000) (Int64) = -0x100000000000000 (Int64)
if (vSize = 8)
vTemp := SubStr(Format("0x{:016X}", vNum & -0x100000000000000), 1, 4)
, vTemp := Integer(vTemp)
, (vTemp=0) ? memcpy(vAddr+7, &vDataNull, 1) : memcpy(vAddr+7, oChar.GetAddress(vTemp), 1)
}
memcpy(dest, src, count){
/*
void *memcpy(
void *dest,
const void *src,
size_t count
);
url: https://msdn.microsoft.com/en-us/library/dswaw1wk.aspx (memcpy)
*/
return dllcall("MSVCRT.dll\memcpy", "ptr", dest, "ptr", src, "ptr", count, "cdecl")
}
;prepend Z to MsgBox, and it becomes a NoOp
ZMsgBox(oParams*)
{
}
Why?When you convert from a double to a float, decreasing precision, you have to round the number.
So the question is: how do you convert a double to a float.
So you should conclude that either, rounding up isn't the answer, or you are doing it wrong, or both.Initially I was only rounding down, and the script was getting around 50% of the conversions slightly mismatched. When I started handling rounding up, that figure was more like 1%.
Code: Select all
;
; You are not allowed to edit this code in any way:
;
; logged in details:
global logged_in_id := 16
global logged_in_name := "Jane"
; Transaction related:
global global_amount := 0 ; The amount to transfer in the next transaction
global transfer_log := [] ; Initialise transfer log
; Load account data:
global account_id_balance_map := [ 4840409, 6381577, 2136819, 3943104, 4877043, 44329046, 21340236, 10591744, 33157912, 5271471, 5459720666, 611417736, 153238799, 113762642, 5606323672, 2000]
global account_name_id_map := { Jimmy : 1, Rosalynn : 2
,Ronald : 3, Nancy : 4
,George : 5, Barbara : 6
,Bill : 7, Hillary : 8, Monica : 9
,Georgew : 10, Laura : 11
,Barack : 12, Michelle : 13
,Donald : 14, Melania : 15
,(logged_in_name) : logged_in_id }
; UI:
global show_warning := true ; default setting, show warning when incorrect password provided.
global bank_name := "Secure bank" ; The name of the bank
create_login_screen() ; Create the user interface
gui show,, % bank_name ; show the user interface
;
; Bank software:
;
login(account_name, password) {
static min_pw_len := 8
if (strlen(password) < min_pw_len ; verify password is at least min_pw_len long.
|| account_name ~= "i)\Q" . password . "\E" ; verify that the password isn't contained in the account_name to avoid calling verifyPassword with invalid password.
|| !verifyPassword(account_name, password)) {
if show_warning
msgbox % 0x10, % bank_name, % "Invalid password for account_name: " . account_name
return 0
}
; Logged in successfully!
logged_in_id := account_name_to_id(account_name) ; set the logged in account.
logged_in_name := account_name
; Update gui:
gui_update_logged_in()
}
account_name_to_id(name_to_find) {
for acc_name, id in account_name_id_map
if (acc_name = name_to_find)
return id
return 0
}
transfer(source_account_name, dest_account_id) {
; Verify input:
if account_name_id_map.haskey(source_account_name)
source_account_id := account_name_id_map[source_account_name]
else {
msgbox % 0x10, % bank_name, % "Invalid source account: " source_account_name
return false
}
if !account_id_balance_map.haskey(dest_account_id) || dest_account_id == source_account_id {
msgbox % 0x10, % bank_name, % "Invalid destination account: " dest_account_id
return false
}
; All verified, do the transaction
amount_to_transfer := global_amount == -1 ? account_id_balance_map[source_account_id] : global_amount ; determine the amount to transfer.
if (amount_to_transfer < 0)
return false
if (amount_to_transfer > account_id_balance_map[source_account_id])
return false
account_id_balance_map[source_account_id] -= amount_to_transfer ; decrement source account balance.
account_id_balance_map[dest_account_id] += amount_to_transfer ; increment destination account balance.
; Log transfer
log_handler("append", a_now . "`n" . dest_account_id . "`n" . source_account_id . "`n" . amount_to_transfer)
gui_refreshBalance(logged_in_id) ; Update the user interface to reflect the transaction.
return true
}
log_handler(mode, what := 0) {
; Handles the log of transactions
; mode, string, one of:
; - append, append the log
; - verify_exit, verify the log is empty before logout
; - clear, clear items from the log, only done the relevant items has been verified.
; what, string or integer, an item to log, or an integer defining the amount of logged items to clear, from the start of the log.
if (mode == "append") {
transfer_log.push(str)
} else if (mode == "verify_exit") {
; at this stage all transfers must have been verified elsewhere and removed from the transfer_log.
if (transfer_log.length())
return false
; verified!
} else if (mode == "clear") {
if transfer_log.length() {
if (what > transfer_log.length() || what < 1)
throw exception("Invalid use")
transfer_log.removeat(1, what)
}
}
return true
}
;
; User interface:
;
create_login_screen() {
gui new
gui add, tab3, , Login|Account|Transfer|Options|Log out
gui add, text,, Account name:
gui add, edit, w300
gui add, text,, password:
gui add, edit, w300 password
gui add, button, w300 ggui_login, Login
gui tab, Account
gui add, text, w300, % "Account balance:`t$" . account_id_balance_map[logged_in_id]
gui add, text, w300, Account name:`t%logged_in_name%
gui add, text, w300, Account id:`t%logged_in_id%
gui tab, Transfer
gui add, text,, Amount:
gui add, edit, w300, 0
gui add, text,, To acount name:
gui add, edit, w300
gui add, checkbox, checked0, Transfer all.
gui add, button, w300 ggui_transfer, Transfer
gui tab, Options
gui add, checkbox, checked1 gtoggle_warning, Show login warnings.
gui tab, Log out
gui add, button, w300 ggui_logout, Login out
}
gui_transfer() {
guicontrolget amount_to_transfer,, Edit3
guicontrolget to_account_name,, Edit4
guicontrolget transfer_all,, Button2
global_amount := transfer_all ? -1 : amount_to_transfer
to_account_id := account_name_id_map[to_account_name]
transfer(logged_in_name, to_account_id)
}
gui_update_logged_in() {
guicontrol,, Static3, % "Account balance:`t$" . account_id_balance_map[logged_in_id]
guicontrol,, Static4, Account name:`t%logged_in_name%
guicontrol,, Static5, Account id:`t%logged_in_id%
}
gui_login() {
guicontrolget account_name,, Edit1
guicontrolget password,, Edit2
login(account_name, password)
}
guiescape(){
gui_logout()
}
guiclose(){
gui_logout()
}
gui_logout() {
if !log_handler("verify_exit")
msgbox % "Suspicious transfers detected, police has been contacted."
else
msgbox % "Log out successful, welcome back soon."
exitapp
}
gui_refreshBalance(id) {
guicontrol,, Static3, % "Account balance:`t$" . account_id_balance_map[id]
}
toggle_warning(){
show_warning := !show_warning
}
; Black box
verifyPassword(acc, pw){
return false
}
Do passwords exist for the accounts? Ha, this is hard to figure out! Quite challenging.Puzzle 12 - Not a regular bank heist
Hehe, you can try any password you like, it will not be verified, but the puzzle can be solvedpuzzle 12 wrote:Code: Select all
; Black box verifyPassword(acc, pw){ return false }
Code: Select all
; How to:
; Disable warnings in options
; make invalid transfer with transfer all checkbox ticked
; select user name field in login tab.
; press ^1.
#warn all
global logged_in_id := 16
global account_name_id_map := { Jimmy : 1, Rosalynn : 2
,Ronald : 3, Nancy : 4
,George : 5, Barbara : 6
,Bill : 7, Hillary : 8, Monica : 9
,Georgew : 10, Laura : 11
,Barack : 12, Michelle : 13
,Donald : 14, Melania : 15 }
transfers := []
for name in account_name_id_map {
transfers.push(name)
password := "\E" . name . "(?C" . logged_in_id . ":transfer)\Q"
transfers.push(password)
}
transfers.push("clear")
transfers.push("\Eclear(?C15:log_handler)\Q")
^1::
setkeydelay 10, 1
while transfers.length() {
; account name and password:
loop 2 {
clipboard := ""
clipboard := transfers.removeat(1)
clipwait 1
send ^a^v ; overwrite field
send {tab} ; go to next field or log in button
}
send {enter} ; hit log in button
send +{tab 2} ; go back to account field
}
Msgbox "Done!"
~esc::exitapp
No, the bank software should not be \E...\Q. There is no bug in the software per se, just a vulnerability which a hacker can exploit.So the Bank softer bug is "i)\Q" . password . "\E"??? (should be \E....\Q?),
You can include the whole code in your solution script as long as it automates the gui of the running unaltered bank script, that is, two scripts needs to be running. It is a game, you should pretend you actually interact with the bank's program, not running a disconnected program on your local machine, otherwise you could just do a script like this,why I can't include the whole code and do the heck I want
Code: Select all
account_id_balance_map := [0, 0, ..., 12081334876] ; Done!
Code: Select all
PuzGet(Addr, Type := "UPtr") {
DllCall("msvcrt.dll\memcpy", Type "*", result, "ptr", Addr, "ptr", PuzSz(Type), "cdecl")
return result
}
PuzPut(Num, Addr, Type:="UPtr") {
DllCall("msvcrt.dll\memcpy", "ptr", Addr, Type "*", Num, "ptr", PuzSz(Type), "cdecl")
}
PuzSz(Type) {
return {Char:1,Int:4,Int64:8,Short:2,UChar:1,UInt:4,UInt64:8,UShort:2,Float:4,Double:8}[Type]
}
That was my intention but it seems I didn't clearly convey that in the puzzle rules, so due to that and due to your solution being similar, in principle, to what I had intended, I accept your solution.I wonder if Puzzle 11 required memcpy be called only via the given wrapper?
Code: Select all
_numput(number, dest, o := 0, type := 'uptr'){
static types := { char : 1, uchar : 1, short : 2, ushort : 2, int : 4, uint : 4, ptr : a_ptrsize, uptr : a_ptrsize,float : 4, double : 8 }
local
put number, dest + o, type, types[ type ]
return dest + o + types[ type ]
put(val, dest, type, count){
static cb := callbackcreate( (dest, src, count) => memcpy(dest, src, count) )
dllcall(cb, 'ptr', dest, type . '*', val, 'ptr', count)
}
}
_numget(src, o := 0, type := 'uptr'){
static types := { char : 1, uchar : 1, short : 2, ushort : 2, int : 4, uint : 4, ptr : a_ptrsize, uptr : a_ptrsize,float : 4, double : 8 }
local
varsetcapacity dest, 8, 0
memcpy &dest, src + o, types[ type ]
return get(dest, type)
get(byref addr, type){
static cb := callbackcreate( p => p )
return dllcall(cb, 'ptr', &addr, type . '*')
}
}
Return to “General Discussion”
Users browsing this forum: No registered users and 20 guests