No specific binary does the hashing; it's done by a library call crypt(3). So, in theory, any program (even a perl script) can generate the hash. e.g.
perl -e 'print crypt("hello","\$6\$0abcdef")'
$6$0abcdef$JhsHMeFo8zYQPa8/HDXGoMWZgIxiCJKu2BQqCGjkyh/NadMax6GiJWQOT5ZL7POkUzrwbvL/Yhbx9f8XtLr.F/
You can read more about this function by man 3 crypt.
Now a normal Unix login process is a bit more complicated. Typically we use the PAM ("Plugin"Pluggable Authentication Modules") stack. This, along with NSS ("network service switch""Name Service Switch"), allows a lot of flexibility in how and where passwords are stored and how authentication is done.
When using PAM, the program itself doesn't call crypt(), it calls the PAM stack and that decides what to use.
A very common pattern is to use pam_unix and it will be in pam_unix.so that the call to crypt() is done, and where the comparison to the /etc/shadow (or LDAP or wherever) would be done.
This PAM/NSS combination is very powerful because it means new methods of authentication can be added (e.g. joining Active Directory domains) without every program needing to know about it; just update the PAM configuration and "magic" happens.