This is my original MSVC code. It has never been posted anywhere else.
It allows you to decrypt the backup configuration file of HT802 that contains the password (and every other setting).
This backup configuration file is created through the web interface of the Grandstream HT802 SIP gateway, f.v.: 1.0.63.3 (and probably other models too). It is useful for recovering the password.
Code:
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 /* Windows 7 SP1 */
#endif
#include
#include
#include
#include
#include
#pragma comment(lib, "bcrypt.lib")
#ifndef NT_SUCCESS
#define NT_SUCCESS(s) (((NTSTATUS)(s)) >= 0)
#endif
#define CHUNK_SIZE ((SIZE_T)32)
#define AES_KEY_SIZE ((ULONG)16)
#define AES_IV_SIZE ((ULONG)16)
/* Pair-swapped key/IV -- see README.md of the Python source for why these
* differ from the firmware's raw byte view. */
static const BYTE g_key[16] = {
'l','e','j','k','a','s','l','f',
0, 0, 0, 0, 0, 0, 0, 0
};
static const BYTE g_iv0[16] = {
'G','r','a','n','d','s','t','r','e','a','m',' ','I','n','c','.'
};
/* Decrypt `n` bytes of `data` in place (n must be a multiple of CHUNK_SIZE).
* Each 32-byte chunk gets a fresh copy of the IV, so chunks are
* independent of each other -- exactly what the Python code does by
* re-creating the cipher object every iteration. */
static NTSTATUS
decrypt_buffer(BYTE* data, SIZE_T n)
{
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
NTSTATUS status;
SIZE_T off;
status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
if (!NT_SUCCESS(status)) goto done;
status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
(PUCHAR)BCRYPT_CHAIN_MODE_CBC,
sizeof(BCRYPT_CHAIN_MODE_CBC), 0);
if (!NT_SUCCESS(status)) goto done;
status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0,
(PUCHAR)g_key, AES_KEY_SIZE, 0);
if (!NT_SUCCESS(status)) goto done;
for (off = 0; off < n; off += CHUNK_SIZE) {
BYTE iv[16];
ULONG produced = 0;
/* BCryptDecrypt overwrites the IV buffer with the last ciphertext
* block on return; refresh it for every chunk. */
memcpy(iv, g_iv0, AES_IV_SIZE);
status = BCryptDecrypt(hKey,
data + off, (ULONG)CHUNK_SIZE,
NULL, /* no padding info */
iv, AES_IV_SIZE, /* IV (in/out) */
data + off, (ULONG)CHUNK_SIZE, /* in-place output */
&produced, 0); /* dwFlags=0 -> no pad */
if (!NT_SUCCESS(status)) goto done;
if (produced != (ULONG)CHUNK_SIZE) {
status = (NTSTATUS)0xC0000001L; /* STATUS_UNSUCCESSFUL */
goto done;
}
}
done:
if (hKey) BCryptDestroyKey(hKey);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
return status;
}
/* Read entire file `path` into a HeapAlloc'd buffer. On success, *out_data
* is the buffer (or NULL if size==0) and *out_size is its length. */
static BOOL
read_whole_file(LPCTSTR path, BYTE** out_data, SIZE_T* out_size)
{
HANDLE h;
LARGE_INTEGER fsize;
BYTE* buf;
SIZE_T total, off;
*out_data = NULL;
*out_size = 0;
h = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
_ftprintf(stderr, _T("Error: cannot open '%s' for reading (Win32 %lu).\n"),
path, GetLastError());
return FALSE;
}
if (!GetFileSizeEx(h, &fsize)) {
_ftprintf(stderr, _T("Error: GetFileSizeEx failed (Win32 %lu).\n"),
GetLastError());
CloseHandle(h);
return FALSE;
}
#if !defined(_WIN64)
if (fsize.HighPart != 0) {
_ftprintf(stderr, _T("Error: file too large for 32-bit build.\n"));
CloseHandle(h);
return FALSE;
}
#endif
total = (SIZE_T)fsize.QuadPart;
if (total == 0) {
CloseHandle(h);
return TRUE;
}
buf = (BYTE*)HeapAlloc(GetProcessHeap(), 0, total);
if (!buf) {
_ftprintf(stderr, _T("Error: out of memory (need %Iu bytes).\n"), total);
CloseHandle(h);
return FALSE;
}
/* ReadFile takes a DWORD count, so issue the read in <=1 GiB chunks. */
for (off = 0; off < total; ) {
SIZE_T remain = total - off;
DWORD want = (DWORD)((remain > 0x40000000u) ? 0x40000000u : remain);
DWORD got = 0;
if (!ReadFile(h, buf + off, want, &got, NULL)) {
_ftprintf(stderr, _T("Error: ReadFile failed (Win32 %lu).\n"),
GetLastError());
HeapFree(GetProcessHeap(), 0, buf);
CloseHandle(h);
return FALSE;
}
if (got == 0) break; /* unexpected truncation */
off += got;
}
CloseHandle(h);
*out_data = buf;
*out_size = off;
return TRUE;
}
/* Write `size` bytes from `data` to `path`, replacing any existing file. */
static BOOL
write_whole_file(LPCTSTR path, const BYTE* data, SIZE_T size)
{
HANDLE h;
SIZE_T off;
h = CreateFile(path, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
_ftprintf(stderr, _T("Error: cannot open '%s' for writing (Win32 %lu).\n"),
path, GetLastError());
return FALSE;
}
for (off = 0; off < size; ) {
SIZE_T remain = size - off;
DWORD want = (DWORD)((remain > 0x40000000u) ? 0x40000000u : remain);
DWORD wrote = 0;
if (!WriteFile(h, data + off, want, &wrote, NULL) || wrote != want) {
_ftprintf(stderr, _T("Error: WriteFile failed (Win32 %lu).\n"),
GetLastError());
CloseHandle(h);
return FALSE;
}
off += wrote;
}
CloseHandle(h);
return TRUE;
}
static int
decrypt_file(LPCTSTR in_path, LPCTSTR out_path)
{
BYTE* data = NULL;
SIZE_T size = 0;
SIZE_T n;
NTSTATUS status;
int rc = 0;
if (!read_whole_file(in_path, &data, &size))
return 1;
n = size - (size % CHUNK_SIZE);
if (n > 0) {
status = decrypt_buffer(data, n);
if (!NT_SUCCESS(status)) {
_ftprintf(stderr, _T("Error: AES decryption failed (NTSTATUS 0x%08lX).\n"),
(unsigned long)status);
rc = 1;
goto out;
}
}
if (!write_whole_file(out_path, data, size)) {
rc = 1;
goto out;
}
_tprintf(_T("Decryption of %s complete (%Iu full %u-byte blocks).\n"),
in_path, n / CHUNK_SIZE, (unsigned)CHUNK_SIZE);
out:
if (data) HeapFree(GetProcessHeap(), 0, data);
return rc;
}
static LPCTSTR
basename_of(LPCTSTR p)
{
LPCTSTR last = p;
LPCTSTR s;
for (s = p; *s; ++s)
if (*s == _T('\\') || *s == _T('/'))
last = s + 1;
return last;
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc == 2)
return decrypt_file(argv[1], argv[1]);
if (argc == 3)
return decrypt_file(argv[1], argv[2]);
LPCTSTR prog = (argc >= 1 && argv[0]) ? basename_of(argv[0]) : _T("decrypt_cfg.exe");
_ftprintf(stderr,
_T("Decrypts Grandstream HT802 Backup Configuration file.\n")
_T("Usage: %s FILE (decrypt FILE in place)\n")
_T(" %s INPUT_FILE OUTPUT_FILE (decrypt INPUT_FILE -> OUTPUT_FILE)\n"),
prog, prog);
return 1;
}
The attached RAR file contains an extensive readme file explaining how this was reverse-engineered and the same C source code (with includes intact) for the decrypter and C code for the encrypter (a reverse operation) and some working Python code and compiled executable binaries .