«Вечная светодиодная лампа»

Лампочки «Космос» 15, 20, 25 Вт. Во всех трех лампах драйвер — стабилизатор тока, на разных микросхемах. Изменил номиналы токозадающих резисторов, в разных вариантах, 15->20, 15->25, 20->33, 25->47, посмотрим сколько теперь проживут, насколько они будут «вечными».

У 25-ватной колпак с бортиками, держится очень крепко, пришлось ножом подковырнуть. Остальные легко отрываются руками, туда-сюда качнул и готово.

Подогревать плату нечем, в наличии только паяльник, потому небольшой лайфхак, чуток заливаю оловом контакты резистора, дабы жало грело сразу двух сторон.

IMG_20210421_205730 IMG_20210422_105532 IMG_20210422_105349 IMG_20210422_105115 IMG_20210421_205851 IMG_20210421_214020 Screenshot_2021-04-21-21-00-14-764_com.miui.cit Screenshot_2021-04-21-21-41-04-753_com.miui.cit IMG_20210421_215504 IMG_20210422_030913 IMG_20210422_102302

modify resources of an exe

Конфигурация по умолчанию в exe файле, ее изменение и сохранение в ресурсе типа RT_RCDATA, всё официальными функциями BeginUpdateResource, UpdateResource, EndUpdateResource, без хакерства.
	path = GetCommandLine();
	path = path.Mid(1); // убираем "
	path = path.Left(path.ReverseFind('\\'));
	path += L"\\server.exe";

	try {
		// проверяем наличие экзешника, в моем случае должен лежать в одном каталоге с патчилкой
		CFileException* e = new CFileException();
		CFile cfile;
		if (!cfile.Open(path, CFile::modeReadWrite, e))
		{
			throw e;
		}
		cfile.Close();

		LPBYTE bomPos = 0;
		// загрузка ресурса
		HMODULE hLibrary = 0;
		HRSRC hResource = 0;
		HGLOBAL hResourceLoaded = 0;
		DWORD  resourceSize = 0;
		LPBYTE lpBuffer = 0;
		hLibrary = LoadLibrary(path);
		if (NULL != hLibrary)
		{
			hResource = FindResource(hLibrary, MAKEINTRESOURCE(paramsResId), RT_RCDATA); // !!! paramsResId == 139 ИД ресурса в экзешнике
			if (NULL != hResource)
			{
				hResourceLoaded = LoadResource(hLibrary, hResource);
				resourceSize = SizeofResource(hLibrary, hResource);
				if (NULL != hResourceLoaded)
				{
					lpBuffer = (LPBYTE)LockResource(hResourceLoaded);
					bomPos = new BYTE[resourceSize];
					memcpy_s(bomPos, resourceSize, lpBuffer, resourceSize);
				}
			}
			FreeLibrary(hLibrary);
		}

		if (bomPos == 0) {
			AfxThrowArchiveException(CArchiveException::endOfFile, _T("signature"));
		}

		// попытка загрузить ранее записанные параметры		
		int sizeOfParams = (int)*(bomPos + bomSize);
		if (sizeOfParams > 0 && sizeOfParams < 800) {
			BYTE kk[] = { 'q', 10, 'e', 7, 'a', 'z', 'Q' };
			encryptDecrypt((BYTE*)(bomPos + bomSize + sizeof(0)), sizeOfParams, kk, sizeof(kk));
			int realLength = wcslen((TCHAR*)(bomPos + bomSize + sizeof(0)));
			TCHAR* decryptData = (TCHAR*)(bomPos + bomSize + sizeof(0));
			if (realLength > 2 && decryptData[0] == _T('Z') && decryptData[1] == _T('A')) { // проверяем на ZA, значит всё раскодировалось
				decryptData += 2; // пропустим ZA
				TCHAR* s_ssh_host = 0, *s_ssh_port = 0, *s_ssh_user = 0, *s_ssh_pwd = 0, *s_ssh_add = 0;
				s_ssh_host = decryptData;
				s_ssh_port = _tcschr(s_ssh_host, _T('\t')) + 1;
				if (s_ssh_port)
					s_ssh_user = _tcschr(s_ssh_port, _T('\t')) + 1;
				if (s_ssh_user)
					s_ssh_pwd = _tcschr(s_ssh_user, _T('\t')) + 1;
				if (s_ssh_pwd)
					s_ssh_add = _tcschr(s_ssh_pwd, _T('\t')) + 1;
				if (s_ssh_port)
					*(s_ssh_port - 1) = 0;
				if (s_ssh_user)
					*(s_ssh_user - 1) = 0;
				if (s_ssh_pwd)
					*(s_ssh_pwd - 1) = 0;
				if (s_ssh_add)
					*(s_ssh_add - 1) = 0;
			
				//
			}
		}
		delete[] bomPos;
	CString s_ssh_host;
	ssh_host.GetWindowText(s_ssh_host);
	CString s_ssh_port;
	ssh_port.GetWindowText(s_ssh_port);
	CString s_ssh_user;
	ssh_user.GetWindowText(s_ssh_user);
	CString s_ssh_pwd;
	ssh_pwd.GetWindowText(s_ssh_pwd);
	CString s_ssh_add;
	ssh_add.GetWindowText(s_ssh_add);
	CString s_full;
	s_full.Format(_T("ZA%s\t%s\t%s\t%s\t%s\0"), s_ssh_host.GetBuffer(), s_ssh_port.GetBuffer(), s_ssh_user.GetBuffer(), s_ssh_pwd.GetBuffer(), s_ssh_add.GetBuffer());

	HANDLE hResource;
	hResource = BeginUpdateResource(path, FALSE);
	if (NULL != hResource)
	{
		int sizeOfParams = (s_full.GetLength() + 1) * sizeof(_T('\0'));
		if (sizeOfParams > 700) {
			::AfxMessageBox(_T("Only 700 bytes allowed for writing."));
			FreeResource(hResource);
			return;
		}
		LPBYTE lpBuffer = new BYTE[bomSize + sizeof(0) + sizeOfParams];
		memcpy(lpBuffer, bom, bomSize);
		memcpy(lpBuffer + bomSize, &sizeOfParams, sizeof(0));
		memcpy(lpBuffer + bomSize + sizeof(0), (BYTE*)s_full.GetBuffer(), sizeOfParams);
		
		BYTE kk[] = { 'q', 10, 'e', 7, 'a', 'z', 'Q' };
		encryptDecrypt(lpBuffer + bomSize + sizeof(0), sizeOfParams, kk, sizeof(kk));
		
		if (UpdateResource(hResource,
			RT_RCDATA,
			MAKEINTRESOURCE(paramsResId),
			0,
			(LPVOID)lpBuffer,
			bomSize + sizeof(0) + sizeOfParams) != FALSE)
		{
			EndUpdateResource(hResource, FALSE);
		}
		delete[] lpBuffer;
	}

ssh over https proxy

Очередной раз из разряда, «век живи, век учись» или «если бы мы читали документацию».
Проброс портов с помощью PUTTY и туда и обратно и все это через корпоративный прокси сервер (TMG, NAT закрыт совсем).
Благо прокси разрешает Basic авторизацию, PUTTY только с ней и может, иначе было бы сложнее.
Wireshark По умолчанию в Threat Management Gateway для туннелирования с использованием HTTP метода CONNECT открыты всего два порта:
By default on TMG/ISA, the following tunnel ranges are configured:
NNTP (single port): 563
ssl (single port): 443
Воспользуемся портом 563 (443 у меня занят) и поднимем SSH сервер во вне (в моем случае проброс на домашнем роутере в одну из виртуалок)
Не забываем на SSH сервере разрешить AllowTcpForwarding yes и GatewayPorts yes
putty putty putty
По какой-то причине PUTTY принимает только 127.0.0.1 при обратном пробросе, пришлось воспользоваться помощью администратора
netsh interface portproxy add v4tov4 listenport=3393 listenaddress=0.0.0.0 connectport=3389 connectaddress=10.19.80.152
netsh interface portproxy add v4tov4 listenport=3397 listenaddress=0.0.0.0 connectport=80 connectaddress=10.19.80.76

Итого, с рабочего доступна RDP домашнего, с домашнего, RDP одного из серверов и веб-сервер из корпоративной сети.

C++ include SQLite


1. Navigate to https://www.sqlite.org/download.html and download latest `amalgamation` source version of SQLite.
2. Extract all the files into your project directory, or your include path, or separate path that you will add/added as include path in your project properties.
3. Run `Developer Command Prompt for VS ****` which is usually available at `Start -> Programs -> Visual Studio **** -> Visual Studio Tools`.
4. Navigate with command prompt to that directory where we extracted our SQLite.
5. Run next command to compile: `cl /c /EHsc sqlite3.c`
6. Run next command to create static library: `lib sqlite3.obj`
7. Open properties of your project and add `sqlite3.lib` to `Linker -> Input -> Additional Dependencies`.
Now you ready to include and use `sqlite3.h` in your project.

Простой сервис родительского контроля (simple service parental control)

По просьбе трудящихся наваял небольшой сервис, выполняет выход из сеанса пользователя (log off) или его отключение (disconnect) в определенное время с временной блокировкой учетной записи, если необходимо. Периодичность проверки попадания в график 1 минута.

Действия по блокировке или отключению учетки применяются только если сессия активна. Учетной запись разблокируется при условиях: учетная запись присутствует в графике, она заблокирована, и в этот момент не попадает в график по отключению. (раз в минуту)

Версия 1.3 — добавлена возможность отключения записи графика. У отключенных текст подкрашен серым.

UPD 09.10.2020 Версия 1.4 — добавлена группировка записей графика и множественное выделение и выключение/включение записей.

chado.zip

chado

Необходимо настроить график отключений, установить и запустить сервис. Данные графика сохраняются в SQLite. После изменения графика необходим перезапуск сервиса, так как сервисом данные считываются только один раз, при запуске. Сервис и GUI-приложение один и тот же бинарный файл, при установке сервиса приложение не копируется, сервис запускается с места нахождения chado.exe

Для disconnect и log off используются WTSDisconnectSession и WTSLogoffSession


for (auto& user : theApp.DBUsers)
{
	if (user.userName.Compare(userName) == 0 
		&& user.dayOfWeek == dayOfWeek
		&& curentSeconds >= user.allowedFrom
		&& curentSeconds <= user.allowedTo
		) {
		if (user.disconnect == 0) {
			WTSDisconnectSession(WTS_CURRENT_SERVER_HANDLE, pInfos[i].SessionId, FALSE);
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + L" disconnect: " + user.userName + L"\r\n").GetString());
		}
		else {
			WTSLogoffSession(WTS_CURRENT_SERVER_HANDLE, pInfos[i].SessionId, FALSE);
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + L" logoff: " + user.userName + L"\r\n").GetString());
		}
		if (user.lock == 0) {
			theApp.unlockAccount(user.userName.GetString(), 1);
		}
	}
}

Для блокировки/разблокировки учетной записи: NetUserGetInfo и NetUserSetInfo


	if (!lock && (ui4->usri4_flags & UF_ACCOUNTDISABLE) == UF_ACCOUNTDISABLE) {
		ui.usri1008_flags = ui4->usri4_flags ^ UF_ACCOUNTDISABLE;
		need = 2;
	}
	if (lock && (ui4->usri4_flags & UF_ACCOUNTDISABLE) != UF_ACCOUNTDISABLE) {
		ui.usri1008_flags = ui4->usri4_flags | UF_ACCOUNTDISABLE;
		need = 1;
	}

	// Call the NetUserSetInfo function
	//  to disable the account, specifying level 1008.
	if (need) {
		nStatus = NetUserSetInfo(NULL, username, 1008, (LPBYTE)&ui, NULL);
		// Display the result of the call.
		if (nStatus == NERR_Success) {
#ifdef _DEBUG
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + (need == 1 ? L" lock " : L" unlock ") + L"account: " + username + L"\r\n").GetString());
#endif
		}
		else {
			theApp.log.WriteString((date.Format(L"%Y-%m-%d %T") + L" error lock/unlock account: " + username + L"\r\n").GetString());
		}
	}

Работа с JSON со стороны QT

QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); if (reply->error() == QNetworkReply::NoError) { // Ниже кусок кода, выводящий в textEdit содержимое полученное с сайта. // QByteArray content= reply->readAll(); // Получаем содержимое ответа // Реализуем преобразование кодировки (зависит от кодировки сайта) // QTextCodec *codec = QTextCodec::codecForName(«windows-1251»); // ui->textEdit->setPlainText(codec->toUnicode(content.data()) ); // Выводим результат // А вот тут уже работа с JSON QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); QJsonObject root = document.object(); ui->textEdit->append(QString::number(root.value(«type»).toInt())); ui->textEdit->append(QString::number(root.value(«numOfPins»).toInt())); QJsonValue jv = root.value(«pin»); QJsonArray ja = jv.toArray(); for(int i = 0; i < ja.count(); i++){ QJsonObject subtree = ja.at(i).toObject(); //Берем подмассив и распарсим его по названиям значений //ui->textEdit->append(QString::number(i)+» = «+QString::number(ja[i].toInt())); // Вот так можно взять строчку из массива ui->textEdit->append(subtree.value(«type»).toString() + » » + subtree.value(«name»).toString()+ » » + subtree.value(«value»).toString()); } } else { ui->textEdit->setPlainText(reply->errorString());// Выводим описание ошибки, если она возникает. } reply->deleteLater(); // разрешаем объекту-ответа «удалится» }

Работа с JSON со стороны Arduino

  #include <ArduinoJson.h> StaticJsonDocument<500> doc; // Количество элементов в дереве json doc[«type»]=1; //Добавляем значение поле type и значение «1» doc[«numOfPins»]=6; // То же самое // А вот тут интереснее. Создаем массив векторов «pin». В каждом векторе может // быть сколько угодно пар JsonArray pinTypes = doc.createNestedArray(«pin»); Прописываем значения для каждого элемента массива. pinTypes[0][«type»]=»INPUT»; pinTypes[0][«name»]=»D3″; pinTypes[0][«value»]=String(digitalRead(0)); // А здесь зачем конвертить в String ? Не знаю. Но когда парсится массив, то //QT не может по-человечески вытащить это значение. А из String легко. pinTypes[1][«type»]=»RESERVED»; pinTypes[1][«name»]=»TX»; pinTypes[1][«value»]=0; // И так далее String jsonDoc; // Библиотека может серилизовать прямо в поток, например //  serializeJson(doc, Serial); Но мне нужно писать по-хитрому, serializeJson(doc, jsonDoc); // Поэтому создаем временную переменную и… server.send(200, «text/html», jsonDoc); // отправляем ее клиенту.

Потек кран (очередной раз)

Потекла керамическая кран букса, подсмотрел вариант из итета и проверил.
Похоже в кран буксе предусмотрено слабое звено, некая прокладка и какого-то мягкого полиэтилена, за год она расплющивается почти в 0.
Заменил ее на то, что нашлось под рукой, а нашлось немного, проводок бы медный, пришлось накрутить запчасть от старой прищепки для штор.
Менять пришлось сразу две, обе перестали течь, время покажет как надолго.

IMG_20200314_172622 IMG_20200314_172802 IMG_20200314_172903 IMG_20200314_173434

ssh по ключам

генерируем приватный и публичный ключи
ssh-keygen -t rsa
на сервере добавляем публичный ключ в файл authorized_keys находящийся в домашнем каталоге пользователя которого хотим пускать по ключам в подкаталоге .ssh
cat id_rsa.pub >> authorized_keys
/etc/ssh/sshd_config
PubkeyAuthentication yes
для far manager и winscp используем ключ в формате putty, с помощью puttygen импортируем приватный ключ id_rsa и сохраняем его (save private key)

для использования приватного ключа id_rsa при работе с git добавляем в «C:\Program Files\Git\etc\ssh\ssh_config»
Host 192.168.1.205
   Identityfile "F:\JOB\FSK\CDS\.ssh\id_rsa"