Sniffing ports is slow: you have to send a bunch of packets and use a timeout to know which ports are open and which are not. Also, it is very uncommon for the list of open ports on a server to change every 2 minutes. Steganography cannot exist if it implies unusual setups; it strives on inconspicuousness.
What you could do is to hide data in source ports. Whenever you send an UDP packet to a target server, the packet is tagged with the target port (which is often well known, e.g. 53 for a DNS request) and a source port. The source port is momentarily open on the client and meant to receive the answer, should an answer be sent (there again, this is a normal situation with DNS). Under normal conditions, the source port is chosen at random by the client; applications don't need to bother with it, it is allocated by the kernel). But the application could choose the source port (it is a matter of a
bind() system call before sending the packet) and thus convey information in a covert manner to the server.
That way, you can send covert information to the server under the guise of innocent-looking DNS requests. To mimic normal random port allocation as performed by operating systems, you would have to restrain yourself to roughly the same range, so you would be able to send, say, about 12 bits of information per requests. To make the covert transfer undetectable, you would have to encrypt the data (the source ports must "look random" and nothing looks more random than encrypted data).
It is a generic property: whenever a data protocol includes elements which are supposed to be chosen randomly by the server, then it can be subverted into a covert data channel. This is usually considered to be a security issue, because it allows the sender to leak critical data to third parties without the receiver noticing it. An example is the padding of record data in SSL 3.0 (section 18.104.22.168), when a block cipher is used: the value of the padding bytes can be chosen arbitrarily by the sender. This was fixed in TLS 1.0, where the value of the padding bytes is enforced:
Each uint8 in the padding data
vector must be filled with the padding length value.
I don't know of any standard for doing steganography through port numbers. When you come down to it, it looks pretty useless. Also, by nature, "steganography" does not mix well with "standards".