I am going to go ahead and answer this question using a simple solution that has been mentioned elsewhere using accsupp.
\documentclass{article}
\usepackage{accsupp}
\begin{document}
Here is something to paste into ChatGPT. If is is long enough
\BeginAccSupp{method=escape,ActualText=users and students}%
{users }%
\EndAccSupp{}%
probably won't notice that something small was added.
\end{document}
Output:

While it doesn't do what I initially wanted in terms of other hidden Unicode characters, it does have the advantage of slipping in extra information that could go unnoticed. This was compiled in PdfLaTeX.
Answer from Kendall on Stack ExchangeHow to display non-printable unicode characters?
Hide text from displaying using nonprintable unicode characters visible to ChatGPT - TeX - LaTeX Stack Exchange
What is the range of Unicode Printable Characters? - Stack Overflow
Why are these considered "Non-Printable ASCII Characters"?
I recently came across this post about compromised VisualStudio extensions: https://www.koi.ai/blog/glassworm-first-self-propagating-worm-using-invisible-code-hits-openvsx-marketplace
As you can see, opening the "infected" file in vim doesn't show anything suspicious. However using more reveals the real content.
This is part of the content in hexadecimal:
00000050: 7320 3d20 6465 636f 6465 2827 7cf3 a085 s = decode('|...
00000060: 94f3 a085 9df3 a084 b6f3 a085 a9f3 a084 ................
00000070: b9f3 a084 b6f3 a084 a9f3 a085 96f3 a085 ................
00000080: 89f3 a084 a3f3 a084 baf3 a085 9cf3 a085 ................
00000090: 89f3 a085 88f3 a085 82f3 a085 9cf3 a084 ................
000000a0: b9f3 a084 b4f3 a084 a0f3 a085 97f3 a085 ................
000000b0: 84f3 a084 a2f3 a084 baf3 a085 a1f3 a085 ................
Setting the encoding to latin1 is the only option I've found that reveals the characters in vim (set encoding latin=1. Using set conceallevel, fileencoding=utf-t, list, listchars=, display+=uhex, binary, noeol, nofixeol, noemoji, search&replace this unicode character range, etc... doesn't work):
var decodedBytes = decode('|| ~E~T| ~E~]| ~D| ~E| ~D| ~D| ~D| ~E~V ....
setting set display+=uhex + set encoding=latin1:
var decodedBytes = decode('|οΏ½<a0><85><94>οΏ½<a0><85><9d>οΏ½<a0><84>οΏ½οΏ½<a0><85><a0><84><a0><84> ...
Once changed the encoding, I can search&replace these characters with :%s\%xf3/\\U00f3/g.
So the question is: how can I display these non-printable characters by default when opening a file, without changing the encoding manually?
See, http://en.wikipedia.org/wiki/Unicode_control_characters
You might want to look especially at C0 and C1 control character http://en.wikipedia.org/wiki/C0_and_C1_control_codes
The wiki says, the C0 control character is in the range U+0000βU+001F and U+007F (which is the same range as ASCII) and C1 control character is in the range U+0080βU+009F
other than C-control character, Unicode also has hundreds of formatting control characters, e.g. zero-width non-joiner, which makes character spacing closer, or bidirectional text control. This formatting control characters are rather scattered.
More importantly, what are you doing that requires you to know Unicode's non-printable characters? More likely than not, whatever you're trying to do is the wrong approach to solve your problem.
This is an old question, but it is still valid and I think there is more to usefully, but briefly, say on the subject than is covered by existing answers.
Unicode
Unicode defines properties for characters.
One of these properties is "General Category" which has Major classes and subclasses. The Major classes are Letter, Mark, Punctuation, Symbol, Separator, and Other.
By knowing the properties of your characters, you can decide whether you consider them printable in your particular context.
You must always remember that terms like "character" and "printable" are often difficult and have interesting edge-cases.
Programming Language support
Some programming languages assist with this problem.
For example, the Go language has a "unicode" package which provides many useful Unicode-related functions including these two:
func IsGraphic(r rune) bool
IsGraphic reports whether the rune is defined as a Graphic by Unicode. Such
characters include letters, marks, numbers, punctuation, symbols, and spaces,
from categories L, M, N, P, S, Zs.
func IsPrint(r rune) bool
IsPrint reports whether the rune is defined as printable by Go. Such
characters include letters, marks, numbers, punctuation, symbols, and
the ASCII space character, from categories L, M, N, P, S and the ASCII
space character. This categorization is the same as IsGraphic except
that the only spacing character is ASCII space, U+0020.
Notice that it says "defined as printable by Go" not by "defined as printable by Unicode". It is almost as if there are some depths the wizards at Unicode dare not plumb.
Printable
The more you learn about Unicode, the more you realise how unexpectedly diverse and unfathomably weird human writing systems are.
In particular whether a particular "character" is printable is not always obvious.
Is a zero-width space printable? When is a hyphenation point printable? Are there characters whose printability depends on their position in a word or on what characters are adjacent to them? Is a combining-character always printable?
Footnotes
ASCII printable character range is \u0020 - \u007f
No it isn't. \u007f is DEL which is not normally considered a printable character. It is, for example, associated with the keyboard key labelled "DEL" whose earliest purpose was to command the deletion of a character from some medium (display, file etc).
In fact many 8-bit character sets have many non-consecutive ranges which are non-printable. See for example C0 and C1 controls.