# Full version
# This attempts to extract the password from older WD drives by dumping
# module 42. It provides possible passwords based on a likely pattern
# that proceeds the passwords. You then have the option to try the password
# to unlock the drive or disable security, or to try to find more possible
# passwords. If no more possible passwords are found it will exit.


# set the 8 search bytes
# these are the 8 bytes that likely preceed the user password
seti $searchbyte0 = 0x00
seti $searchbyte1 = 0x00
seti $searchbyte2 = 0x00
seti $searchbyte3 = 0x00
seti $searchbyte4 = 0x00
seti $searchbyte5 = 0x00
seti $searchbyte6 = 0x00
seti $searchbyte7 = 0x00



# this subroutine is put here so following commands recognize variables
subroutine get_security_status
  buffersize 512
  protocol pio-data-in
  direction from
  ata28cmd 0 0 0 0 0 0xa0 0xec
  if $ata_return_error != 0
    gosub error
  endif
  seti $tempnum = buffer 257
  seti $security_level = $tempnum & 1
  seti $tempnum = buffer 256
  seti $enhanced_security_erase_supported = $tempnum & 32
  seti $security_count_expired = $tempnum & 16
  seti $security_frozen = $tempnum & 8
  seti $security_locked = $tempnum & 4
  seti $security_enabled = $tempnum & 2
  seti $security_supported = $tempnum & 1
  if $security_supported > 0
    echo "security_supported= yes"
  else
    echo "security_supported= no"
  endif
  if $enhanced_security_erase_supported > 0
    echo "enhanced_security_erase_supported= yes"
  else
    echo "enhanced_security_erase_supported= no"
  endif
  if $security_frozen > 0
    echo "security_frozen= yes"
  else
    echo "security_frozen= no"
  endif
  if $security_count_expired > 0
    echo "security_count_expired= yes"
  else
    echo "security_count_expired= no"
  endif
  if $security_enabled > 0
    echo "security_enabled= yes"
  else
    echo "security_enabled= no"
  endif
  if $security_locked > 0
    echo "security_locked= yes"
  else
    echo "security_locked= no"
  endif
endsubroutine



gosub get_security_status
if $security_enabled = 0
  echo "Security is not enabled on this drive, nothing to do."
  previousscript
endif

echo "Trying to dump module 42 (older drives)"
seti $mod_length_bytes = 1024
seti $dumperror = 0
gosub dump_mod_42
if $dumperror = 1
  gosub show_ata_return
  echo "Failed getting module 42"
  previousscript
endif

#gosub read_file

seti $finished = 0
seti $position = 0
gosub find_password
if $finished = 1
  previousscript
endif

while 1 = 1
  echo "What would you like to do?"
  echo "1) Try to unlock with user password"
  echo "2) Try to unlock with master password"
  echo "3) Try to remove security with user password"
  echo "4) Try to remove security with with master password"
  echo "5) Continue trying to find the next possible password"
  echo "6) Quit"
  echo "Enter your choice:
  userinput $choicestring
  seti $choice = $choicestring 0
  if $choice = 1
    if $security_locked = 0
      echo "Drive is already unlocked!"
    else
      seti $type = 0
      gosub unlock
      gosub get_security_status
      if $security_locked = 0
        echo "Drive unlocked!"
      endif
    endif
  elseif $choice = 2
    if $security_locked = 0
      echo "Drive is already unlocked!"
    else
      seti $type = 1
      gosub unlock
      gosub get_security_status
      if $security_locked = 0
        echo "Drive unlocked!"
      endif
    endif
  elseif $choice = 3
    seti $type = 0
    gosub remove_password
    gosub get_security_status
    if $security_enabled = 0
      echo "Security has been disabled! Nothing more to do."
      previousscript
    endif
  elseif $choice = 4
    seti $type = 1
    gosub remove_password
    gosub get_security_status
    if $security_enabled = 0
      echo "Security has been disabled! Nothing more to do."
      previousscript
    endif
  elseif $choice = 5
    seti $position = $position + 1
    gosub find_password
    if $finished = 1
      previousscript
    endif
  elseif $choice = 6
    previousscript
  else
    echo "bad choice= " $choice
  endif
done


previousscript
end


subroutine error
  echo "***Drive reported command error***"
  #gosub show_cmd_status
  gosub show_sense_data
  gosub show_ata_return
  previousscript
endsubroutine


subroutine read_file
  readscratchpad 22.bin 0 0 512
  seti $mod_length_bytes = 512
endsubroutine


subroutine find_password
  seti $max_start = $mod_length_bytes - 68
  while $position < $max_start
    seti $position1 = $position + 1
    seti $position2 = $position + 2
    seti $position3 = $position + 3
    seti $position4 = $position + 4
    seti $position5 = $position + 5
    seti $position6 = $position + 6
    seti $position7 = $position + 7
    seti $position8 = $position + 8
    seti $byte0 = scratchpad $position
    seti $byte1 = scratchpad $position1
    seti $byte2 = scratchpad $position2
    seti $byte3 = scratchpad $position3
    seti $byte4 = scratchpad $position4
    seti $byte5 = scratchpad $position5
    seti $byte6 = scratchpad $position6
    seti $byte7 = scratchpad $position7
    seti $byte8 = scratchpad $position8
    if $byte0 = $searchbyte0
      if $byte1 = $searchbyte1
        if $byte2 = $searchbyte2
          if $byte3 = $searchbyte3
            if $byte4 = $searchbyte4
              if $byte5 = $searchbyte5
                if $byte6 = $searchbyte6
                  if $byte7 = $searchbyte7
                    if $byte8 != 0
                      seti $userpassword = $position7 + 33
                      seti $masterpassword = $position7 + 1
                      echo "possible user password:"
                      printscratchpad $userpassword 32
                      echo "possible master password:"
                      printscratchpad $masterpassword 32
                      returnsub
                    endif
                  endif
                endif
              endif
            endif
          endif
        endif
      endif
    endif
    seti $position = $position + 1
  done
  seti $finished = 1
  echo "No more passwords to be found"
endsubroutine




subroutine dump_mod_42
  seti $dumperror = 0
  protocol non-data
  direction none
  buffersize 0
  ata28cmd 0x57 0x44 0x43 0x00 0x00 0xa0 0x8a
  if $ata_return_error != 0
    gosub show_ata_return
    seti $dumperror = 1
    returnsub
  endif
  protocol pio-data-in
  direction from
  buffersize $mod_length_bytes
  ata28cmd 0x00 0x02 0x00 0x00 0x0F 0xE0 0x21
  if $ata_return_error != 0
    gosub show_ata_return
    seti $dumperror = 1
    returnsub
  endif
  printbuffer 0 $mod_length_bytes
  scratchpadsize $mod_length_bytes
  copybuffertoscratchpad 0 0 $mod_length_bytes
  # for some reason the disk needs to be closed and reopened
  # after the dump so that the security commands will work
  reopendisk
endsubroutine




subroutine remove_password
  # set the buffer size
  buffersize 512
  # clear the buffer to 0's
  clearbuffer

  # put the type in the buffer
  seti $byte0 = $type & 0x01
  setbuffer 0
    $byte0
  endbuffer

  if $byte0 = 0
    copyscratchpadtobuffer $userpassword 2 32
  else
    copyscratchpadtobuffer $masterpassword 2 32
  endif

  # set the LBAs and count to 0
  seti $count = 0
  seti $LBAlow = 0
  seti $LBAmid = 0
  seti $LBAhigh = 0

  # set some things for the ata command
  protocol pio-data-out
  tlengthfield sectorcount
  transferunit block
  direction to
  # set features to 0
  seti $features = 0
  # set device bits 7(compatibility) 5(compatibility)
  seti $device = 0xa0
  # set the command for security disable password pio data-out
  seti $command = 0xf6

  # perform the command
  ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $command
  if $ata_return_error != 0
    gosub error
  endif
endsubroutine


subroutine unlock
  buffersize 512
  clearbuffer
  # put the type in the buffer
  seti $byte0 = $type & 0x01
  setbuffer 0
    $byte0
  endbuffer

  if $byte0 = 0
    copyscratchpadtobuffer $userpassword 2 32
  else
    copyscratchpadtobuffer $masterpassword 2 32
  endif

  # set the LBAs and count to 0
  seti $count = 0
  seti $LBAlow = 0
  seti $LBAmid = 0
  seti $LBAhigh = 0

  # set some things for the ata command
  protocol pio-data-out
  tlengthfield sectorcount
  transferunit block
  direction to
  # set features to 0
  seti $features = 0
  # set device bits 7(compatibility) 5(compatibility)
  seti $device = 0xa0
  # set the command for security unlock pio data-out
  seti $command = 0xf2

  # perform the command
  ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $command
  if $ata_return_error != 0
    gosub error
  endif
endsubroutine




subroutine show_sense_data
  hex
  echo "sense_key=0x" $io_sense_key " asc=0x" $io_asc " ascq=0x" $io_ascq
  decimal
endsubroutine


subroutine show_cmd_status
  echo "status=" $io_status " masked_status=" $io_masked_status " msg_status=" $io_msg_status " sb_len_wr=" $io_sb_len_wr " host_status=" $io_host_status " driver_status=" $io_driver_status " resid=" $io_resid " duration=" $io_duration " info=" $io_info
endsubroutine


subroutine show_ata_return
  hex
  echo "error=0x" $ata_return_error " count=0x" $ata_return_count " lba=0x" $ata_return_lba " device=0x" $ata_return_device " status=0x" $ata_return_status
  decimal
endsubroutine
