Indeed... dd is an exact sector-by-sector copier unless you tell it to do otherwise. For small damaged source disks/partitions/files you can just do it all in single sector mode:
dd if=<source> of=<destination> conv=sync,noerror bs=512
The bs=512 should be optional since that is the default, but you might need to specify a blocksize explicitly it if, for example, the source is a CD where you would need 2048 as that is the actual sector size. Many newer hard disks use 4096 byte sectors, though they can "emulate" 512-byte sector accesses.
The sync,noerror conversion types mean fill with NULLs if I can't read a spot instead of truncating the file (and you DON'T WANT it to truncate if you are copying a whole disk, obviously!!) and continue copying after an error.
With large disks, though, copying the whole thing one 512-byte sector at a time is going to be incredibly slow so it is better to copy in 128k blocks (or whatever the maximum block size is for the OS you're on) first, THEN go back and fill in the spots that are missing 512-bytes at a time.
This can be done manually using the iseek= and oseek= (seek / skip) options in conjunction with the count= option to put missing bits in specified places but there is the ddrescue tool which does it for you automatically, reading in big chunks first then automatically going back and retrying missing bits afterwards, keeping track of where it is in case you have to abort and resume later, etc. VERY handy.
ddrescue has been included in the base FreeBSD system for years but should easily be available on pretty much any platform and is definitely the way to go if you are trying to image a failing drive.