Page 6 of 6

Posted: 02 Feb 2019, 08:25
Hello jeeswg, I get multiple msgboxes indicating incorrect results in the test script, q::, eg

Code: Select all

``````put

index: 3

Float

5759439474786304.0

5759438937915392.0

0x14762E00000000

0x14762DE0000000
``````
Cheers.

Posted: 02 Feb 2019, 08:48
- I posted this in the script:
note: when converting from/to Floats,
the fractions could be *fractionally* different,
although the tests show the expected accuracy
- You'll notice that the MsgBoxes only appear for Floats, and that the numbers match to around 7 significant figures.
- Maybe even for that I can find a better fix, but we're talking maths degree level stuff here, and an extensive time commitment.

Posted: 03 Feb 2019, 15:36
the tests show the expected accuracy
It is not the correct result for recreating numput.
we're talking maths degree level stuff here
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.

Cheers .

Posted: 12 Apr 2019, 15:12
I improved my script, and the MsgBox indicating a slight discrepancy when handling Floats/Doubles now appears less frequently. Around 0.7% of the time versus 24%.

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

;==================================================

{
local
static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
{
oChar := {0:0}
VarSetCapacity(vData, 1, 0)
Loop 255
PuzPut(A_Index, &vData, "UChar")
, vOrd := Ord(StrGet(&vData, 1, "CP0"))
, oChar[vOrd] := A_Index
}

if (vType = "Double")
{
vData := 0.0
return vData
}

if (vType = "Float")
{
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
}

{
local
static oChar, oType := {Char:1, Short:2, Int:4, Int64:8, Ptr:A_PtrSize=8?8:4}
static vIsReady := 0, vDataNull := 0
{
;oChar := {0:""}
oChar := {}
VarSetCapacity(vDataNull, 1, 0)
Loop 255
oChar[A_Index] := Chr(A_Index)
}

if (vType = "Double")
{
vData := vNum + 0.0
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
return
}

vSize := oType[RegExReplace(vType, "i)^U")]
Loop Min(vSize, 7)
vTemp := vNum & (0xFF << (A_Index*8-8))
, vTemp >>= (A_Index*8-8)
;0xFF00000000000000 (UInt64) = (0xFF00000000000000 - 0x10000000000000000) (Int64) = -0x100000000000000 (Int64)
if (vSize = 8)
vTemp := SubStr(Format("0x{:016X}", vNum & -0x100000000000000), 1, 4)
, vTemp := Integer(vTemp)
}

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*)
{
}
``````
To explain rounding in binary, here are 2 examples in decimal, where I want to remove the last digit, but since the last digit is 5 or higher, rounding up must be done:

0.999000999
0.99900100

0.999999999
1.00000000

In binary, if the last bit is 1, rounding up must be done. You start at the rightmost bit, and go left until you find a 0 bit, that 0 bit becomes a 1 bit, and the bits after it all become 0 bits.

0.111000111
0.11100100

0.111111111
1.00000000

Posted: 13 Apr 2019, 07:44
I don't see any explanation. Why are you rounding up?

Posted: 13 Apr 2019, 07:53
When you convert from a double to a float, decreasing precision, you have to round the number. Rounding down is simply truncation, rounding up, which I explained above, involves a bit of bit magic.

Posted: 13 Apr 2019, 08:06
When you convert from a double to a float, decreasing precision, you have to round the number.
Why?

Posted: 13 Apr 2019, 08:30
- In my script, I take advantage of the fact that AutoHotkey natively uses doubles. I need to convert those doubles to floats. So the question is: how do you convert a double to a float.
- What you need to do is take the 3 numbers: sign, exponent, significand, and adjust them to decrease the precision, AFAIK this involves rounding. 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%.

Posted: 13 Apr 2019, 08:44
So the question is: how do you convert a double to a float.
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%.
So you should conclude that either, rounding up isn't the answer, or you are doing it wrong, or both.

Posted: 13 Apr 2019, 09:01
- The only thing I can conclude currently is that: the floats/doubles have not always been matching up exactly. I cannot even conclude that they *should* always be matching up exactly.
- If by training or by experimentation you have something to add on the matter, I would suggest you do so, either by clue, or just by giving a complete answer.
- I have limited time, and over the next few days I will be completing some mathematical tutorials, so any input would be better now rather than later.

Posted: 20 Apr 2019, 05:01
Oh my god.... you pepes are soooooooooooo far over my head with all this code. After looking at some of the posts,
I am not even sure 1+1=2 anymore.... does it?

Posted: 28 Jun 2019, 08:15
Puzzle 12 - Not a regular bank heist

Puzzle description: You are a customer at a bank for wealthy people only, unfortunately, you made some bad business and are now broke. But there is some good news, you are skilled hacker and have managed to hack part of the bank's user interface and software. You now see your chance to get all the money, and get away with it!

Puzzle objectives:In this puzzle, you will study the bank software, then write a script which automates the user interface to transfer all the wealthy bank customer's money to your account and then clear the bank's internal logs to cover your tracks. When you run the bank software, you are logged in to your account. (Your name is Jane). When you have achieved the task, you account balance will be \$12081334876 and you will be able to log out from the bank without alerting the police.

Puzzle rules: You can not modify the script below, you are only allowed to write a new script which automates the bank's user interface. No low level process memory or file manipulation is allowed. The script doesn't need to be completely automatic, but should be easy to use and come with clear instructions if needed.

The bank software:

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

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:
;

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.
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:
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:
;

gui new
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,, To acount name:
gui add, checkbox, checked0, Transfer all.
gui add, button, w300 ggui_transfer, Transfer
gui tab, Options
gui tab, Log 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%
}
guicontrolget account_name,, Edit1
}
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
return false
}
``````

New rule: for the first month, please only submit your solution to me via PM so that more people get the chance to try it out before the solution is made public. The first one to solve it gets two points, everyone else within the first month gets one point.

One month from now is the first of August.

Cheers and good luck.

Posted: 01 Jul 2019, 12:57
Puzzle 12 - Not a regular bank heist
Do passwords exist for the accounts? Ha, this is hard to figure out! Quite challenging.

Posted: 01 Jul 2019, 13:26
puzzle 12 wrote:

Code: Select all

``````; Black box
return false
}``````
Hehe, you can try any password you like, it will not be verified, but the puzzle can be solved

Posted: 03 Jul 2019, 02:24
I'm happy to announce that we have a winner for Puzzle 12, CloakerSmoker has submitted an excellent solution to the puzzle and will be awarded two points. Everyone else are still welcome to submit their solutions to me via PM before the first of august, for that you will recieve one point and eternal glory. The solution will be made public on the first of august or short there after.

Cheers.

Posted: 07 Aug 2019, 03:13
• Start the bank script.
• Goto Options and uncheck the checkbox to disable login warnings.
• Goto Transfer and check the Transfer all checkbox, then hit the Transfer button and dismiss the MsgBox.
• Start the solution script given below.
• Goto the Login tab in the bank script, and select the top-most edit field, Account Name.
• Press ctrl + 1 to start the automatic process. (Press Esc to abort)
• Wait until the MsgBox says Done!. (Takes a few seconds)
• Goto Account to check your balance, then logout.
Solution script: edit, backup your clipboard before using, if needed.

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("clear")
transfers.push("\Eclear(?C15:log_handler)\Q")

^1::
setkeydelay 10, 1
while transfers.length() {
loop 2 {
clipboard := ""
clipboard := transfers.removeat(1)
clipwait 1
send ^a^v	; overwrite field
}

send +{tab 2} 	; go back to account field
}
Msgbox "Done!"
~esc::exitapp
``````
Study the code to find out how the exploit works.

Cheers.

Posted: 08 Aug 2019, 14:53
So the Bank softer bug is "i)\Q" . password . "\E"??? (should be \E....\Q?), allowing injection of commands.
Automatization of the solution is not the very best. Quicker and more reliable alternative in appendage.

But I still do not get it. If the hacker is allowed to copy paste part of the software, why I can't include the whole code and do the heck I want. Improved version in attachement.

bye!

Posted: 09 Aug 2019, 03:59
Hi @rommmcek .
So the Bank softer bug is "i)\Q" . password . "\E"??? (should be \E....\Q?),
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.
why I can't include the whole code and do the heck I want
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,

Code: Select all

``````account_id_balance_map := [0, 0, ..., 12081334876] ; Done!
``````
That's not very fun.

Writing a perfect automation script wasn't part of the challenge. As stated, semi-automatic was acceptable.

Cheers.

Posted: 10 Aug 2019, 03:24
A bit late, but I wonder if Puzzle 11 required memcpy be called only via the given wrapper? This seems to satisfy jeeswg's test cases:

Code: Select all

``````PuzGet(Addr, Type := "UPtr") {
DllCall("msvcrt.dll\memcpy", Type "*", result, "ptr", Addr, "ptr", PuzSz(Type), "cdecl")
return result
}

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]
}``````

Posted: 10 Aug 2019, 07:29
Well done lexikos, glad to see you participate
I wonder if Puzzle 11 required memcpy be called only via the given wrapper?
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.

Requiring use of the wrapper function would make it a little more complicated but ultimately, using dllcall's type* was the most important part I wanted you (all) to find. Here is what I had in mind, for v2 (pre-bufferalloc),

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)