What I would do is hold the function to apply in the wrapper type.
import qualified Data.ByteString as BS
data ByteString' a = ByteString' (Word8 -> a) BS.ByteString
wrap :: BS.ByteString -> ByteString' Word8
wrap bs = ByteString' id bs
-- The type ensures you can only unwrap with a function Word8 -> Word8.
unwrap :: ByteString' Word8 -> ByteString
unwrap (ByteString' f bs) = BS.map f bs
-- Functor instance just concatenates the fmapped function.
instance Functor ByteString' where
fmap f (ByteString' g bs) = ByteString' (f . g) bs
-- Foldable instance just uses the fmapped function.
instance Foldable ByteString' where
foldr f z (ByteString' g bs) = BS.foldr (f . g) z bs
-- You could define foldr', foldl, etc. based on the ones in Data.ByteString.
-- Not strictly necessary, but nice to have.
As an added benefit, this doesn't require GADTs. It probably would if you wanted to implement Monad as well, though.