Check out the new blog at http://linuxehacking.ovh , this one will no longer be updated

sabato 12 luglio 2014

Alcor UFD Controller Reverse engineering part 1 , querying info and decrypting the flash list shipped in AlcorMP tool

I've started that work because i want to port AlcorMP utility to Linux and to be able to use custom flash chips with these UFD chips, this utility called AlcorMP allows to do a lot of stuff , from checking flash integrity to programming the usb flash drive into a CD-Rom emulator.

For who is new to that field, these flash drives store configuration data and badblocks on an hidden sector of the flash memowy which normally is not visible by the end user.

To program that sector you have to issue vendor specific SCSI commands, the ones that i've found are:
0x9a: Seems to return 0x200 bytes of data still not reverse engineered
0xfa00: Seems to return 0x200 bytes of data too, but this one returns the Flash Chip identification as the first 6 bytes , so it's something useful.
There's also 0xf5 that is still unknown

Now the hard part, first thing you will see when you try to figure out where the program takes the flash part.no and vendor, is that there's no plaintext list with that data, and there's no compressed data either.
Analyzing it with binwalk gives a very discouraging entropy graph as shown below
flashlist.afl entropy plot using binwalk -E


After some work on the UfdComLib.dll , it turns out that the file flashlist.afl has been encrypted with a block cipher on purpose.

Lucky the program itself ( except LLF.dll that is encrypted too ), is not obfuscated , so it has been relatively easy extracting the encryption algorithm from the program and use it to decode the flashlist.afl .

The function on UfdComLib.dll that gives an huge help locating the decryption code with IDA is the one at 0x100022F0

.text:100022F0 .text:100022F0 ; =============== S U B R O U T I N E ======================================= .text:100022F0 .text:100022F0 ; Attributes: bp-based frame .text:100022F0 .text:100022F0 ; int __cdecl sub_100022F0(LPCSTR lpFileName) .text:100022F0 sub_100022F0 proc near ; CODE XREF: sub_10001D80+292p .text:100022F0 .text:100022F0 ReturnedString = byte ptr -14Ch .text:100022F0 var_44 = byte ptr -44h .text:100022F0 var_40 = byte ptr -40h .text:100022F0 var_3C = byte ptr -3Ch .text:100022F0 var_38 = byte ptr -38h .text:100022F0 var_36 = byte ptr -36h .text:100022F0 var_34 = byte ptr -34h .text:100022F0 var_32 = byte ptr -32h .text:100022F0 var_30 = byte ptr -30h .text:100022F0 var_2E = byte ptr -2Eh .text:100022F0 AppName = byte ptr -2Ch .text:100022F0 var_24 = dword ptr -24h .text:100022F0 var_20 = dword ptr -20h .text:100022F0 var_1C = dword ptr -1Ch .text:100022F0 var_18 = dword ptr -18h .text:100022F0 var_14 = dword ptr -14h .text:100022F0 var_10 = dword ptr -10h .text:100022F0 var_C = dword ptr -0Ch .text:100022F0 var_4 = dword ptr -4 .text:100022F0 hFile = dword ptr 8 .text:100022F0 .text:100022F0 push ebp .text:100022F1 mov ebp, esp .text:100022F3 push 0FFFFFFFFh .text:100022F5 push offset SEH_100022F0 .text:100022FA mov eax, large fs:0 .text:10002300 push eax .text:10002301 mov large fs:0, esp .text:10002308 sub esp, 140h .text:1000230E push ebx .text:1000230F push esi .text:10002310 push edi .text:10002311 mov edi, [ebp+hFile] .text:10002314 xor esi, esi .text:10002316 mov [ebp+var_10], esp .text:10002319 push esi ; hTemplateFile .text:1000231A push 80h ; dwFlagsAndAttributes .text:1000231F push 3 ; dwCreationDisposition .text:10002321 push esi ; lpSecurityAttributes .text:10002322 push 1 ; dwShareMode .text:10002324 push 80000000h ; dwDesiredAccess .text:10002329 push edi ; lpFileName .text:1000232A mov [ebp+var_14], esi .text:1000232D mov [ebp+var_4], esi .text:10002330 call ds:CreateFileA .text:10002336 cmp eax, 0FFFFFFFFh .text:10002339 jz short loc_10002371 .text:1000233B push eax ; hObject .text:1000233C call ds:CloseHandle .text:10002342 push 4 ; int .text:10002344 push edi ; lpFileName .text:10002345 call sub_10004220 .text:1000234A mov ebx, eax .text:1000234C add esp, 8 .text:1000234F cmp ebx, esi .text:10002351 mov [ebp+var_24], ebx .text:10002354 ja short loc_100023BD .text:10002356 push offset aError_flash_it ; "ERROR_FLASH_ITEM_COUNT" .text:1000235B lea ecx, [ebp+var_40] .text:1000235E call ??0CString@@QAE@PBD@Z ; CString::CString(char const *) .text:10002363 lea eax, [ebp+var_40] .text:10002366 push offset unk_10006950 .text:1000236B push eax .text:1000236C call _CxxThrowException .text:10002371 ; --------------------------------------------------------------------------- .text:10002371 .text:10002371 loc_10002371: ; CODE XREF: sub_100022F0+49j .text:10002371 push offset asc_100081E4 ; "\r\n" .text:10002376 lea ecx, [ebp+hFile] .text:10002379 call ??0CString@@QAE@PBD@Z ; CString::CString(char const *) .text:1000237E mov esi, eax .text:10002380 push offset aError_file_no_ ; "ERROR_FILE_NO_EXISTS: " .text:10002385 lea ecx, [ebp+var_24] .text:10002388 mov byte ptr [ebp+var_4], 1 .text:1000238C call ??0CString@@QAE@PBD@Z ; CString::CString(char const *) .text:10002391 push edi .text:10002392 push eax .text:10002393 lea eax, [ebp+var_18] .text:10002396 mov byte ptr [ebp+var_4], 2 .text:1000239A push eax .text:1000239B call ??H@YG?AVCString@@ABV0@PBD@Z ; operator+(CString const &,char const *) .text:100023A0 push esi .text:100023A1 lea ecx, [ebp+var_3C] .text:100023A4 push eax .text:100023A5 push ecx .text:100023A6 mov byte ptr [ebp+var_4], 3 .text:100023AA call ??H@YG?AVCString@@ABV0@0@Z ; operator+(CString const &,CString const &) .text:100023AF lea edx, [ebp+var_3C] .text:100023B2 push offset unk_10006950 .text:100023B7 push edx .text:100023B8 call _CxxThrowException .text:100023BD ; --------------------------------------------------------------------------- .text:100023BD .text:100023BD loc_100023BD: ; CODE XREF: sub_100022F0+64j .text:100023BD mov eax, ebx .text:100023BF shl eax, 5 .text:100023C2 add eax, ebx .text:100023C4 lea ecx, [eax+eax*4] .text:100023C7 lea edx, [ebx+ecx*2] .text:100023CA shl edx, 1 .text:100023CC push edx ; unsigned int .text:100023CD call ??2@YAPAXI@Z ; operator new(uint) .text:100023D2 add esp, 4 .text:100023D5 cmp eax, esi .text:100023D7 mov [ebp+var_18], eax .text:100023DA jz short loc_1000240A .text:100023DC mov [ebp+var_14], ebx .text:100023DF mov esi, 296h .text:100023E4 mov edx, eax .text:100023E6 .text:100023E6 loc_100023E6: ; CODE XREF: sub_100022F0+114j .text:100023E6 mov ecx, [ebp+var_14] .text:100023E9 dec ecx .text:100023EA mov [ebp+var_14], ecx .text:100023ED js short loc_10002406 .text:100023EF mov ecx, 0A5h .text:100023F4 xor eax, eax .text:100023F6 mov edi, edx .text:100023F8 add edx, esi .text:100023FA rep stosd .text:100023FC stosw .text:100023FE mov edi, [ebp+hFile] .text:10002401 mov eax, [ebp+var_18] .text:10002404 jmp short loc_100023E6 .text:10002406 ; --------------------------------------------------------------------------- .text:10002406 .text:10002406 loc_10002406: ; CODE XREF: sub_100022F0+FDj .text:10002406 xor esi, esi .text:10002408 jmp short loc_1000240C .text:1000240A ; --------------------------------------------------------------------------- .text:1000240A .text:1000240A loc_1000240A: ; CODE XREF: sub_100022F0+EAj .text:1000240A xor eax, eax .text:1000240C .text:1000240C loc_1000240C: ; CODE XREF: sub_100022F0+118j .text:1000240C push ebx ; int .text:1000240D push esi ; int .text:1000240E push eax ; int .text:1000240F push 4 ; int .text:10002411 push edi ; hFile .text:10002412 mov [ebp+var_14], eax .text:10002415 call sub_100043D0 .text:1000241A add esp, 14h .text:1000241D cmp eax, esi .text:1000241F jnz short loc_1000243C .text:10002421 push offset aError_flashlis ; "ERROR_FlashListApi_ReadItem\r\n" .text:10002426 lea ecx, [ebp+var_44] .text:10002429 call ??0CString@@QAE@PBD@Z ; CString::CString(char const *) .text:1000242E lea eax, [ebp+var_44] .text:10002431 push offset unk_10006950 .text:10002436 push eax .text:10002437 call _CxxThrowException .text:1000243C ; --------------------------------------------------------------------------- .text:1000243C .text:1000243C loc_1000243C: ; CODE XREF: sub_100022F0+12Fj .text:1000243C push 0FFFFFFFFh .text:1000243E push esi .text:1000243F mov ecx, offset unk_10008430 .text:10002444 call sub_10003060 .text:10002449 cmp ebx, esi .text:1000244B jnz short loc_1000249E .text:1000244D mov edi, dword_10008434 .text:10002453 cmp edi, esi .text:10002455 jz short loc_1000248D .text:10002457 mov esi, dword_10008438 .text:1000245D .text:1000245D loc_1000245D: ; CODE XREF: sub_100022F0+180j .text:1000245D mov ecx, esi .text:1000245F dec esi .text:10002460 test ecx, ecx .text:10002462 jz short loc_10002472 .text:10002464 push 0 ; char .text:10002466 mov ecx, edi ; void * .text:10002468 call sub_100040E0 .text:1000246D add edi, 10h .text:10002470 jmp short loc_1000245D .text:10002472 ; --------------------------------------------------------------------------- .text:10002472 .text:10002472 loc_10002472: ; CODE XREF: sub_100022F0+172j .text:10002472 mov edx, dword_10008434 .text:10002478 push edx ; void * .text:10002479 call ??3@YAXPAX@Z ; operator delete(void *) .text:1000247E add esp, 4 .text:10002481 mov dword_10008434, 0 .text:1000248B xor esi, esi .text:1000248D .text:1000248D loc_1000248D: ; CODE XREF: sub_100022F0+165j .text:1000248D mov dword_1000843C, esi .text:10002493 mov dword_10008438, esi .text:10002499 jmp loc_100025C7 .text:1000249E ; --------------------------------------------------------------------------- .text:1000249E .text:1000249E loc_1000249E: ; CODE XREF: sub_100022F0+15Bj .text:1000249E mov edx, dword_10008434 .text:100024A4 cmp edx, esi .text:100024A6 jnz short loc_100024D3 .text:100024A8 mov eax, ebx .text:100024AA shl eax, 4 .text:100024AD push eax ; unsigned int .text:100024AE call ??2@YAPAXI@Z ; operator new(uint) .text:100024B3 add esp, 4 .text:100024B6 mov dword_10008434, eax .text:100024BB push ebx .text:100024BC push eax .text:100024BD call sub_10003DF0 .text:100024C2 mov dword_1000843C, ebx .text:100024C8 .text:100024C8 loc_100024C8: ; CODE XREF: sub_100022F0:loc_10002501j .text:100024C8 ; sub_100022F0+223j .text:100024C8 mov dword_10008438, ebx .text:100024CE jmp loc_100025C7 .text:100024D3 ; --------------------------------------------------------------------------- .text:100024D3 .text:100024D3 loc_100024D3: ; CODE XREF: sub_100022F0+1B6j .text:100024D3 mov ecx, dword_1000843C .text:100024D9 cmp ebx, ecx .text:100024DB jg short loc_10002523 .text:100024DD mov eax, dword_10008438 .text:100024E2 cmp eax, ebx .text:100024E4 jge short loc_10002501 .text:100024E6 mov ecx, ebx .text:100024E8 sub ecx, eax .text:100024EA shl eax, 4 .text:100024ED add eax, edx .text:100024EF push ecx .text:100024F0 push eax .text:100024F1 call sub_10003DF0 .text:100024F6 mov dword_10008438, ebx .text:100024FC jmp loc_100025C7


This function uses another function to read the header and get the number of entries on the flash list file , sub_10004220

.text:10004220 .text:10004220 ; =============== S U B R O U T I N E ======================================= .text:10004220 .text:10004220 .text:10004220 ; int __cdecl sub_10004220(LPCSTR lpFileName, int) .text:10004220 sub_10004220 proc near ; CODE XREF: sub_100022F0+55p .text:10004220 .text:10004220 var_100 = dword ptr -100h .text:10004220 var_DC = dword ptr -0DCh .text:10004220 var_D4 = dword ptr -0D4h .text:10004220 lpFileName = dword ptr 4 .text:10004220 arg_4 = dword ptr 8 .text:10004220 .text:10004220 mov ecx, [esp+lpFileName] .text:10004224 sub esp, 100h .text:1000422A lea eax, [esp+100h+var_100] .text:1000422E push esi .text:1000422F push eax ; int .text:10004230 push ecx ; lpFileName .text:10004231 xor esi, esi .text:10004233 call sub_10004270 .text:10004238 add esp, 8 .text:1000423B test eax, eax .text:1000423D jnz short loc_10004247 .text:1000423F pop esi .text:10004240 add esp, 100h .text:10004246 retn .text:10004247 ; --------------------------------------------------------------------------- .text:10004247 .text:10004247 loc_10004247: ; CODE XREF: sub_10004220+1Dj .text:10004247 mov edx, [esp+104h+var_DC]

This one above checks if the file has been read successfully and if yes, it returns the number of entries of the flash list file.

.text:10004270 .text:10004270 ; =============== S U B R O U T I N E ======================================= .text:10004270 .text:10004270 ; Attributes: bp-based frame .text:10004270 .text:10004270 ; int __cdecl sub_10004270(LPCSTR lpFileName, int) .text:10004270 sub_10004270 proc near ; CODE XREF: sub_10004220+13p .text:10004270 .text:10004270 Buffer = byte ptr -328h .text:10004270 var_228 = byte ptr -228h .text:10004270 var_128 = byte ptr -128h .text:10004270 dwErrCode = dword ptr -28h .text:10004270 NumberOfBytesRead= dword ptr -24h .text:10004270 var_20 = dword ptr -20h .text:10004270 var_1C = dword ptr -1Ch .text:10004270 var_18 = dword ptr -18h .text:10004270 var_14 = dword ptr -14h .text:10004270 var_10 = dword ptr -10h .text:10004270 var_C = dword ptr -0Ch .text:10004270 var_4 = dword ptr -4 .text:10004270 lpFileName = dword ptr 8 .text:10004270 arg_4 = dword ptr 0Ch .text:10004270 .text:10004270 push ebp .text:10004271 mov ebp, esp .text:10004273 push 0FFFFFFFFh .text:10004275 push offset SEH_10004270 .text:1000427A mov eax, large fs:0 .text:10004280 push eax .text:10004281 mov large fs:0, esp .text:10004288 sub esp, 31Ch .text:1000428E push ebx .text:1000428F push esi .text:10004290 mov eax, [ebp+lpFileName] .text:10004293 push edi .text:10004294 xor esi, esi .text:10004296 mov [ebp+var_10], esp .text:10004299 push esi ; hTemplateFile .text:1000429A push 80h ; dwFlagsAndAttributes .text:1000429F mov edi, 1 .text:100042A4 push 3 ; dwCreationDisposition .text:100042A6 push esi ; lpSecurityAttributes .text:100042A7 push edi ; dwShareMode .text:100042A8 push 80000000h ; dwDesiredAccess .text:100042AD push eax ; lpFileName .text:100042AE mov [ebp+var_14], edi .text:100042B1 mov [ebp+var_4], esi .text:100042B4 call ds:CreateFileA .text:100042BA mov ebx, eax .text:100042BC cmp ebx, 0FFFFFFFFh .text:100042BF mov [ebp+lpFileName], ebx .text:100042C2 jnz short loc_100042D5 .text:100042C4 lea ecx, [ebp+var_20] .text:100042C7 push offset unk_100069F0 .text:100042CC push ecx .text:100042CD mov [ebp+var_20], esi .text:100042D0 call _CxxThrowException .text:100042D5 ; --------------------------------------------------------------------------- .text:100042D5 .text:100042D5 loc_100042D5: ; CODE XREF: sub_10004270+52j .text:100042D5 lea edx, [ebp+NumberOfBytesRead] .text:100042D8 push esi ; lpOverlapped .text:100042D9 push edx ; lpNumberOfBytesRead .text:100042DA lea eax, [ebp+Buffer] .text:100042E0 push 200h ; nNumberOfBytesToRead .text:100042E5 push eax ; lpBuffer .text:100042E6 push ebx ; hFile .text:100042E7 call ds:ReadFile .text:100042ED cmp [ebp+NumberOfBytesRead], 200h .text:100042F4 jz short loc_10004307 .text:100042F6 lea ecx, [ebp+var_18] .text:100042F9 push offset unk_100069F0 .text:100042FE push ecx .text:100042FF mov [ebp+var_18], edi .text:10004302 call _CxxThrowException .text:10004307 ; --------------------------------------------------------------------------- .text:10004307 .text:10004307 loc_10004307: ; CODE XREF: sub_10004270+84j .text:10004307 lea edx, [ebp+var_128] .text:1000430D lea eax, [ebp+Buffer] .text:10004313 push edx .text:10004314 push 10h .text:10004316 push offset unk_10006840 .text:1000431B push 100h .text:10004320 push eax .text:10004321 call sub_10004650 .text:10004326 mov esi, [ebp+arg_4] .text:10004329 add esp, 14h .text:1000432C lea ecx, [ebp+var_128] .text:10004332 lea edx, [ebp+var_228] .text:10004338 push esi .text:10004339 push 100h .text:1000433E push ecx .text:1000433F push 100h .text:10004344 push edx .text:10004345 call sub_10004650 .text:1000434A add esp, 14h .text:1000434D mov ecx, 4 .text:10004352 mov edi, offset unk_10006840 .text:10004357 xor eax, eax .text:10004359 repe cmpsd .text:1000435B jz short loc_100043A4 .text:1000435D lea ecx, [ebp+var_1C] .text:10004360 push offset unk_100069F0 .text:10004365 push ecx .text:10004366 mov [ebp+var_1C], 2 .text:1000436D call _CxxThrowException .text:10004372 ; --------------------------------------------------------------------------- .text:10004372 .text:10004372 loc_10004372: ; DATA XREF: .rdata:stru_10006E88o .text:10004372 mov edx, [ebp+dwErrCode] .text:10004375 mov [ebp+var_14], 0 .text:1000437C push edx ; dwErrCode .text:1000437D call ds:SetLastError .text:10004383 mov eax, offset loc_100043A1 .text:10004388 retn .text:10004389 ; --------------------------------------------------------------------------- .text:10004389 .text:10004389 loc_10004389: ; DATA XREF: .rdata:stru_10006E88o .text:10004389 push 0FFh ; dwErrCode .text:1000438E mov [ebp+var_14], 0 .text:10004395 call ds:SetLastError .text:1000439B mov eax, offset loc_100043A1 .text:100043A0 retn .text:100043A1 ; --------------------------------------------------------------------------- .text:100043A1 .text:100043A1 loc_100043A1: ; DATA XREF: sub_10004270+113o .text:100043A1 ; sub_10004270+12Bo .text:100043A1 mov ebx, [ebp+lpFileName] .text:100043A4 .text:100043A4 loc_100043A4: ; CODE XREF: sub_10004270+EBj .text:100043A4 cmp ebx, 0FFFFFFFFh .text:100043A7 jz short loc_100043B0 .text:100043A9 push ebx ; hObject .text:100043AA call ds:CloseHandle .text:100043B0 .text:100043B0 loc_100043B0: ; CODE XREF: sub_10004270+137j .text:100043B0 mov ecx, [ebp+var_C] .text:100043B3 mov eax, [ebp+var_14] .text:100043B6 pop edi .text:100043B7 pop esi .text:100043B8 mov large fs:0, ecx .text:100043BF pop ebx .text:100043C0 mov esp, ebp .text:100043C2 pop ebp .text:100043C3 retn .text:100043C3 sub_10004270 endp ; sp-analysis failed .text:100043C3 .text:100043C3 ; ---------------------------------------------------------------------------


sub_10004270 is interesting, it actually opens the file and reads a 512 byte block, then a function is used multiple times on the data read from the file: sub_10004650

sub_10004650 is just a wrapper around sub_10004680 , and it does only dereference some char ** to char* , so let's checkout sub_10004680


.text:10004680 .text:10004680 ; =============== S U B R O U T I N E ======================================= .text:10004680 .text:10004680 .text:10004680 ; int __cdecl sub_10004680(char *, int, char *, int, char *) .text:10004680 sub_10004680 proc near ; CODE XREF: sub_10004650+19p .text:10004680 .text:10004680 arg_0 = dword ptr 4 .text:10004680 arg_4 = dword ptr 8 .text:10004680 arg_8 = dword ptr 0Ch .text:10004680 arg_C = dword ptr 10h .text:10004680 arg_10 = dword ptr 14h .text:10004680 .text:10004680 push ebx .text:10004681 push ebp .text:10004682 mov ebp, [esp+8+arg_0] .text:10004686 push edi .text:10004687 test ebp, ebp .text:10004689 jz loc_10004750 .text:1000468F mov ebx, [esp+0Ch+arg_8] .text:10004693 test ebx, ebx .text:10004695 jz loc_10004750 .text:1000469B mov edi, [esp+0Ch+arg_10] .text:1000469F test edi, edi .text:100046A1 jz loc_10004750 .text:100046A7 push esi .text:100046A8 push 100h ; unsigned int .text:100046AD call ??2@YAPAXI@Z ; operator new(uint) .text:100046B2 mov esi, eax .text:100046B4 mov eax, [esp+14h+arg_C] .text:100046B8 push esi .text:100046B9 push eax .text:100046BA push ebx .text:100046BB call sub_10004760 .text:100046C0 mov edx, [esp+20h+arg_4] .text:100046C4 add esp, 10h .text:100046C7 xor eax, eax .text:100046C9 xor ecx, ecx .text:100046CB test edx, edx .text:100046CD jbe short loc_10004740 .text:100046CF sub ebp, edi .text:100046D1 mov [esp+10h+arg_0], edx .text:100046D5 .text:100046D5 loc_100046D5: ; CODE XREF: sub_10004680+BAj .text:100046D5 inc eax .text:100046D6 and eax, 0FFh .text:100046DB mov dl, [eax+esi] .text:100046DE mov bl, [eax+esi] .text:100046E1 add edx, ecx .text:100046E3 and edx, 0FFh .text:100046E9 mov ecx, edx .text:100046EB mov dl, [ecx+esi] .text:100046EE xor bl, dl .text:100046F0 mov [eax+esi], bl .text:100046F3 mov dl, bl .text:100046F5 mov bl, [ecx+esi] .text:100046F8 xor bl, dl .text:100046FA mov [ecx+esi], bl .text:100046FD mov dl, bl .text:100046FF mov bl, [eax+esi] .text:10004702 xor bl, dl .text:10004704 mov [eax+esi], bl .text:10004707 mov dl, bl .text:10004709 xor ebx, ebx .text:1000470B and edx, 0FFh .text:10004711 mov bl, [ecx+esi] .text:10004714 add edx, ebx .text:10004716 and edx, 800000FFh .text:1000471C jns short loc_10004726 .text:1000471E dec edx .text:1000471F or edx, 0FFFFFF00h .text:10004725 inc edx .text:10004726 .text:10004726 loc_10004726: ; CODE XREF: sub_10004680+9Cj .text:10004726 mov dl, [edx+esi] .text:10004729 mov bl, [edi+ebp] .text:1000472C xor dl, bl .text:1000472E mov [edi], dl .text:10004730 mov edx, [esp+10h+arg_0] .text:10004734 inc edi .text:10004735 dec edx .text:10004736 mov [esp+10h+arg_0], edx .text:1000473A jnz short loc_100046D5 .text:1000473C mov edi, [esp+10h+arg_10] .text:10004740 .text:10004740 loc_10004740: ; CODE XREF: sub_10004680+4Dj .text:10004740 push esi ; void * .text:10004741 call ??3@YAXPAX@Z ; operator delete(void *) .text:10004746 add esp, 4 .text:10004749 mov eax, edi .text:1000474B pop esi .text:1000474C pop edi .text:1000474D pop ebp .text:1000474E pop ebx .text:1000474F retn .text:10004750 ; --------------------------------------------------------------------------- .text:10004750 .text:10004750 loc_10004750: ; CODE XREF: sub_10004680+9j .text:10004750 ; sub_10004680+15j ... .text:10004750 pop edi .text:10004751 pop ebp .text:10004752 xor eax, eax .text:10004754 pop ebx .text:10004755 retn .text:10004755 sub_10004680 endp .text:10004755 .text:10004755 ; ---------------------------------------------------------------------------

This one is clearly an encryption algorithm, but before having a complete view on the problem there's also another function: sub_10004760
s
.text:10004760 .text:10004760 ; =============== S U B R O U T I N E ======================================= .text:10004760 .text:10004760 .text:10004760 sub_10004760 proc near ; CODE XREF: sub_10004680+3Bp .text:10004760 .text:10004760 arg_0 = dword ptr 4 .text:10004760 arg_4 = dword ptr 8 .text:10004760 arg_8 = dword ptr 0Ch .text:10004760 .text:10004760 push ebx .text:10004761 mov ebx, [esp+4+arg_0] .text:10004765 test ebx, ebx .text:10004767 jz short loc_100047CC .text:10004769 mov ecx, [esp+4+arg_8] .text:1000476D test ecx, ecx .text:1000476F jz short loc_100047CC .text:10004771 push ebp .text:10004772 push esi .text:10004773 push edi .text:10004774 xor edi, edi .text:10004776 xor eax, eax .text:10004778 .text:10004778 loc_10004778: ; CODE XREF: sub_10004760+21j .text:10004778 mov [eax+ecx], al .text:1000477B inc eax .text:1000477C cmp eax, 100h .text:10004781 jb short loc_10004778 .text:10004783 mov ebp, [esp+10h+arg_4] .text:10004787 xor esi, esi .text:10004789 .text:10004789 loc_10004789: ; CODE XREF: sub_10004760+63j .text:10004789 mov eax, esi .text:1000478B xor edx, edx .text:1000478D div ebp .text:1000478F mov al, [edx+ebx] .text:10004792 mov dl, [esi+ecx] .text:10004795 add edi, edx .text:10004797 add eax, edi .text:10004799 and eax, 0FFh .text:1000479E mov edi, eax .text:100047A0 mov al, [edi+ecx] .text:100047A3 xor dl, al .text:100047A5 mov [esi+ecx], dl .text:100047A8 mov al, dl .text:100047AA mov dl, [edi+ecx] .text:100047AD xor dl, al .text:100047AF mov [edi+ecx], dl .text:100047B2 mov al, dl .text:100047B4 mov dl, [esi+ecx] .text:100047B7 xor dl, al .text:100047B9 mov [esi+ecx], dl .text:100047BC inc esi .text:100047BD cmp esi, 100h .text:100047C3 jb short loc_10004789 .text:100047C5 pop edi .text:100047C6 pop esi .text:100047C7 pop ebp .text:100047C8 mov eax, ecx .text:100047CA pop ebx .text:100047CB retn


After studying on it some hours i've figured out that sub_10004760 is a function used to inizialize a vector of length 256 ( 0x100 ) with the encryption key that is later used on the caller function.
With HexRays decompiler it's fairly easy to generate proper C code of these functions and so re-use them.

The file is made of a first 256 byte block , that encrypted using the algorithm above and "ALCORFLASHCFG_SZ" as the key yields other 256 bytes that have to be used as the key for the next 256 byte block inside the file that is the header.

The header contains some useful info like the size of each record , the number of records and what i think to be the version ( 4 ) as follows:

typedef struct {
  char headerMagic[16];
  int unk1;
  int unk2;
  int unk3;
  int unk4;
  int unk5; // 1
  int version; //Confirmed?
  int entry_size; // Confirmed?
  int entry_count;
    
} AlcorFlashListHeader;

The data after the header is organized in entry_size sized blocks and each one is decrypted using the same key obtained to decrypt the header, but , the first keybyte has to be the record number starting from 0 and the last keybyte has to be the bitwise negation of the record number

This gives us records on which i'm still working to figure out the exact format , but what i've defined so far:
typedef struct {
  unsigned char vendor[16];
  unsigned char partno[32];
  unsigned char id[6];
  unsigned char unk1[0xb];
  unsigned char CE;
....

Other fields still have to be reverse engineered, and they are mandatory to be able to write a program that can reprogram these flash drives.

The complete program that can read the flash list is the following:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LOBYTE(v) *((unsigned char*)&v)

#pragma pack(push, 1)
typedef struct {
  char headerMagic[16];
  int unk1;
  int unk2;
  int unk3;
  int unk4;
  int unk5; // 1
  int version; //Confirmed?
  int entry_size; // Confirmed?
  int entry_count;
    
} AlcorFlashListHeader;
typedef struct {
  unsigned char vendor[16];
  unsigned char partno[32];
  unsigned char id[6];
  unsigned char unk1[0xb];
  unsigned char CE;
  /*unsigned char unk2[14];
  unsigned char cache_enabled;*/
  unsigned char unk[0x260-(1+0xb)];
} AlcorFlashListEntry;
#pragma pack(pop)
// It seems to be some kind of block cipher using 0x100 ( 256 bytes) blocks
unsigned char * init_Vector(const unsigned char * enc_key , int keylen, unsigned char * out)
{
    int v4;
    unsigned int v3;
    unsigned int v5;
    unsigned int v6;
    unsigned int v7;

    unsigned char * res = NULL;
    if ( enc_key && out )
    {
        v4 = 0;
        v3 = 0;
        do {
            out[v3] = v3;
            ++v3;

        } while ( v3 < 0x100 );
        v5 = 0;
        do
        {
            v6 = v5 / keylen;
            v7 = v5 % keylen;
            LOBYTE(v6) = enc_key[v5 % keylen];
            LOBYTE(v7) = out[v5];
            v4 = (v7 + v4 + v6) & 0xFF;
            LOBYTE(v7) = out[v4] ^ v7;
            out[v5] = v7;
            LOBYTE(v7) = v7 ^ out[v4];
            out[v4] = v7;
            out[v5++] ^= v7;


        } while ( v5 < 0x100 );
        res = out;
    } else {
        res = 0;
    }
    return res;
}
void * decryptBlock(unsigned char * buffer, int bufferlen, const unsigned char * enc_key, int keylen, unsigned char * output)
{
    unsigned char * v5; // edi@3
    int v6; // eax@4
    int v7; // edx@4
    int v8; // ecx@4
    unsigned char *v9; // esi@4
    int v10; // ebp@5
    unsigned char v11; // bl@6
    unsigned char v12; // bl@6
    unsigned char v13; // bl@6
    unsigned char* result; // eax@8
    int v16; // [sp+10h] [bp+4h]@5

    if ( buffer && enc_key && ( v5 = output, output ))
    {
        v9 = new unsigned char[0x100];
        init_Vector(enc_key, keylen, v9);
        v7 = bufferlen;
        v6 = 0;
        v8 = 0;
        if ( bufferlen )
        {
            v10 = buffer - output;
            v16 = bufferlen;
            do
            {
                v6 = (v6 + 1) & 0xFF;
                LOBYTE(v7) = v9[v6];
                v8 = (v8 + v7) & 0xFF;
                v11 = v9[v8] ^ v9[v6];
                v9[v6] = v11;
                v12 = v11 ^ v9[v8];
                v9[v8] = v12;
                v13 = v12 ^ v9[v6];
                v9[v6] = v13;
                *v5 = v5[v10] ^ v9[(v9[v8] + v13) % 256];
                ++v5;
                v7 = v16 - 1;
            }
            while ( v16-- != 1 );
            v5 = output;
        }
        delete[] v9;
        result = v5;

    }
}
int main(int argc, char **argv) {
    FILE * f = fopen(argv[1],"rb");
    unsigned char buffer[512];
    fread(buffer,512,1,f);
    unsigned char out1[256];
    decryptBlock(buffer,256,(const unsigned char*)"ALCORFLASHCFG_SZ",16,out1);
    unsigned char out2[512];
    decryptBlock(&buffer[256],256,out1,256,out2);
    
   // fwrite(out2,256,1,stdout);
    AlcorFlashListHeader hdr;
    memcpy(&hdr,out2,sizeof(hdr));
    printf("File version: %d\n",hdr.version);
    printf("Entry size: %d\n",hdr.entry_size);
    printf("Entry count: %d\n",hdr.entry_count);
    
    unsigned char * completebuffer = new unsigned char[std::min(1024*1024*128, hdr.entry_size*hdr.entry_count)];
    unsigned char * completebuffer_enc = new unsigned char[std::min(1024*1024*128, hdr.entry_size*hdr.entry_count)];
    memset(completebuffer,0,hdr.entry_size*hdr.entry_count);
    
    int nb = fread(completebuffer_enc,1,hdr.entry_count*hdr.entry_size,f);
    if ( nb != hdr.entry_count*hdr.entry_size )
        abort();
    for ( int i = 0; i < hdr.entry_count; i++ )
    {
        out1[0] = i;
        out1[255] = ~(i + (unsigned char)0 /*a4*/);
        decryptBlock(&completebuffer_enc[hdr.entry_size*i],hdr.entry_size,out1,256,&completebuffer[hdr.entry_size*i]);
    }
    //fwrite(completebuffer,1,hdr.entry_size*hdr.entry_count,stdout);
    
    AlcorFlashListEntry * entries = (AlcorFlashListEntry*)completebuffer;
    for ( int i = 0; i < hdr.entry_count; i++ )
    {
        printf("%s - %s , ID: %02x%02x%02x%02x%02x%02x ",entries[i].vendor,entries[i].partno,entries[i].id[0],entries[i].id[1],entries[i].id[2],entries[i].id[3],entries[i].id[4],entries[i].id[5]);
     /*   if ( !entries[i].cache_enabled )
        {
            printf("-No cache enabled-");
        }*/
        
        printf(" CE = %d, ADD Data:",entries[i].CE);
        for ( int k = 0; k < 32; k++ )
        {
            printf("%02x",entries[i].unk1[k]);
            
        }
        printf("\n");
    }
    
    return 0;
}

If someone is interested in helping me on that work , comment here, or circle me on Google Plus


venerdì 30 maggio 2014

Resurrecting QBell 39" LCD LED TV as VGA display using an MT6820 board

I've got that TV to be trashed, because after some hours it won't turn on anymore, first thing i've checked is the power supply which was fine.
The original board is labeled "T.MSD309.B21B", and apparently it is a chinese universal LVDS LCD driver board.

So i've checked on ebay and that board is available , but it costs a lot of money ( 80+ € excl. shipping ) , and since the old one broke almost in new condition, and i'm pretty sure it is software-broken, it's reasonable to think that the board is flawed and it will break again and again.
Also i couldn't be sure that the panel was in working condition , so 100 € is too much risky considering that and what said above.

I've choosen then, to use an MT6820 board which can be bought from ebay for as low as 6€, it has only VGA input , but it is fine for what i want to use that LCD for.

This board has various options to support most LCD panels, so i've tried all of them, and the one that is closest to perfection is the no. 1.
But it isn't perfect, you will get swapped columns.
While going through various LVDS pinouts i've noticed that a group of signals is marked "EVEN" and another group "ODD", since the number of pixels horizontally is even, it clearly appears to be caused by swapped EVEN and ODD connections.

The glitch caused by swapped EVEN and ODD


When swapping the signals according to the MT6820 pinout, i was very tired and screwed up all the ordering , so i had to find again the correct pinout.
Since the Part.NO of the panel gives 0 results on google , literally, i've spotted the lvds controller board chip manufacter on the panel , which is "CMO" , CMO stands for ChiMei Optoelectronics, but it does not longer exist , however , after some more hard search , i've figured out that it has been acquired by Innolux, and then i've picked the first datasheet of a LED backlit panel 39 inches wide.

After having the right pinout i've reassembled the cable and i've got to the starting point where columns were swapped.

So the next day, after some rest , i've tried again to swap the connections , and it finally works.

Now let get deeper into the details, if you order an MT6820 , you will likely get an LVDS cable that is useless for our work, and a button board that we need instead.

The button board must be connected with the supplied cable even if the pin count does not match, that is because K5 and K6 on the input side are unused.
Make sure that the other input pins and led pins match on the button board.

On the QBell LVDS cable you have to swap all the EVEN pins with ODD pins, to do that , do not cut the wires or do some s**t like that , instead just use some thin object like a needle to extract the contacts from the plastic of the connector on the MT6820 side and swap them all.

Now that you have ( hopefully ) correct LVDS connection, you need to do one more thing , the MT6820 supports only 3.3V and 5V panel , instead at least on this QBell , the panel voltage is 12V , so you have to cut the Red wires ( pins 1,2,3 ) and connect them with the +12V from the power supply( you can see output voltages from the PCB of the power supply , since they are labeled ).

The backlight is not electrically compatible with the MT6820 so you have to just connect both BLON and ADJ pins of the power supply to +5VSB .
The +5V of the MT6820 instead has to be connected on the +5V of the power supply.
To make the power supply power on as soon you plug the TV to the outlet, connect PSON and +5VSB together.

The MT6820 board "installed"



If nothing is wrong you should see "No signal" written in chinese and it should work correctly as a regular PC VGA display.

LCD Panel pinout

LCD Panel pinout
MT6820 pinout










sabato 17 maggio 2014

[AVANZATO] Installare OpenWRT sul router DSL2740B Revisione A1 , Utile anche per debrick

Premetto subito che le informazioni qui riportate sono per chi ha già una buona esperienza.
Altra premessa è il fatto che non è detto che si possa far ritornare il router al suo stato di fabbrica dopo le modifiche descritte in questo articolo, se si vuole avere un punto di ripristino, eseguire prima di flashmem

readmem 0x1f000000 0x20000 /percorso/al/file/di/backup

Questo comando salva i primi 128kb della memoria flash, che comprendono la nvram ed il cfe della D-Link


Per eseguire l'installazione di OpenWRT è necessario come prima cosa sostituire il bootloader, per fare ciò è necessario utilizzare la porta JTAG , che contrariamente a quanto riportato dalla maggior parte degli articoli che trattano questo router, è presente.

La porta JTAG è accessibile sulle piazzole con le seguenti etichette

TestpointJTAG
TP_DG2GND
TP25TDI
TP26TDO
TP27TMS
TP28TCK




Per eseguire il collegamento tramite JTAG è possibile utilizzare un cavo DLC5 sulla porta parallela come descritto qui http://wiki.openwrt.org/doc/hardware/port.jtag.cable.unbuffered , oppure un adattatore Altera USB Blaster , di quest'ultimo anche un clone cinese va bene.

Per programmare la memoria flash , il programma che mi ha funzionato senza problemi è urjtag (http://urjtag.org), di questo programma non va utilizzata la versione 0.10 ,  ma la versione SVN

Una volta eseguiti tutti i collegamenti ed avviato urjtag come root da riga di comando digitando "jtag", è necessario specificare che tipo di cavo si usa, nel caso della porta parallela

cable parallel 0x378 DLC5

mentre nel caso di Altera USB Blaster

cable usbblaster

Poi è necessario identificare il processore, per fare ciò, digitare detect
Dovrebbe apparire un output simile:

jtag> detect
IR length: 5
Chain length: 1
Device Id: 00000110001101011000000101111111 (0x0635817F)
  Manufacturer: Broadcom (0x17F)
  Part(0):      BCM6358 (0x6358)
  Stepping:     V1
  Filename:     /usr/share/urjtag/broadcom/bcm6358/bcm6358

Se dovesse comparire come device Id una stringa di 0 , oppure altri errori , come TDO seems to be stuck at , o roba simile, ricontrollare i collegamenti e che il router sia acceso.

Poi è necessario inizializzare EJTAG in modalità DMA , per fare ciò:

jtag> initbus ejtag_dma 
ImpCode=00000000100000011000100100000100
EJTAG version: <= 2.0
EJTAG Implementation flags: R4k MIPS16 DMA MIPS32
Clear memory protection bit in DCR
Clear Watchdog
Potential flash base address: [0x0], [0x1f00008c]
Processor successfully switched in debug mode.
jtag>

Se dovesse bloccarsi urjtag in questo passo oppure dovesse dire che non riesce a mettere in modalità debug il processore, ritentare e se il problema persiste , provare ad entrare nel prompt CFE utilizzando la porta seriale e ritentare da capo la parte inerente ad urjtag.
È di fondamentale importanza , essendo JTAG stateful , che quando qualcosa vada storto o si vuole ricominciare , sia chiuso e riaperto urjtag e spento e riacceso il router.

Una volta inizializzato EJTAG , si può passare ad inizializzare la flash CFI con il seguente comando

jtag> detectflash 0x1f000000

Dovrebbero essere restituite tutte le informazioni sulla memoria flash , in caso contrario , ritentare più volte e se continua a non funzionare , chiedere aiuto nei commenti di questo post
Un altro passo fondamentale è eseguire il comando

jtag> endian big

Questo comando, imposta che anche il file è bigendian come il processore e quindi urjtag non andrà ad invertire le word

Se tutto è andato a buon fine , si è pronti a scrivere il nuovo CFE sulla memoria flash, per fare ciò , va innanzitutto scaricato il nuovo CFE https://drive.google.com/file/d/0B5uKF4o3fPSWYzZya2paWWdOd2c/edit?usp=sharing e poi va scritto sulla memoria flash con il comando flashmem come segue:


flashmem 0x1f000000 /percorso/completo/a/cfe6358.bin

Se tutto è corretto inizierà a cancellare e programmare i settori della memoria flash con i contenuti del file, una voltè che è completata la procedura , e la fase di verifica non ha dato errori, si è pronti a riavviare il router e tramite la porta seriale configurare i parametri della scheda

Verranno chiesti alcuni parametri come segue:

*** Board is not initialized properly ***

Press:  <enter> to use current value
       '-' to go previous parameter
       '.' to clear the current value
       'x' to exit this command
Board Id Name (0-4)
96358VW          -------  0
96358VW-16       -------  1
96358GW          -------  2
96358GW-16       -------  3
96358M           -------  4       :  0
Number of MAC Addresses (1-32)    :  0  4
Base MAC Address                  :  24:0a:64:xx:xx:xx
Press any key to reset the board:

Sostituire ovviamente le xx del mac address con dei codici esadecimali validi e che non siano un indirizzo di broadcast.
È possibile anche modificare i primi 3 byte del mac address, quelli che ho messo io sono del produttore della mia scheda madre ( ASUS ).

Il reset della scheda al termine dell'operazione non è detto che funzionerà , in tal caso trascorsi almeno 5 secondi , rimuovere e riapplicare l'alimentazione

Una volta completato questo passo, rientrando nuovamente nel prompt del CFE , dovrebbero essersi abilitate le porte ethernet con indirizzo ip 192.168.1.1.
Openwrt deve essere scaricato da http://downloads.openwrt.org/attitude_adjustment/12.09/brcm63xx/generic/openwrt-96358VW-generic-squashfs-cfe.bin o nel caso questa versione sia vecchia in futuro , scaricare comunque una versione che sia squahfs e sia 96358VW.
Una volta scaricato openwrt , connettere il PC con un cavo ethernet allo switch del router, e visitare la pagina http://192.168.1.1

In questa pagina , non serve fare altro che selezionare il file precedentemente scaricato e dare OK, in pochi minuti dovrebbe avviarsi il router con openwrt.

Oltre al modem ADSL che come su tutti i router brcm63xx a causa del fatto che broadcom non rilascia la documentazione. non funziona , potrebbe non funzionare anche la scheda wireless essendo anche essa broadcom, in questo caso, è necessario procurarsi una scheda ath5k od una scheda ath9k minipci ed installarla al posto della broadcom.
Installando una scheda atheros, è possibile utilizzare questo router come access point multi-ssid e multi-vlan ( cose fattibili solo con Cisco che costano minimo 150 € ), ed è possibile anche lavorare sulla banda 5 ghz , quindi utilizzare il router come semplice access point 802.11a oppure utilizzarlo per connettersi ad una rete outdoor 5 ghz , come quella di ninux.org.

Preciso un'ultima cosa inoltre, questa guida probabilmente è applicabile anche ad altri router basati su chip bcm6358 anche se non sono dichiarati compatibili con openwrt ( neanche questo lo è )



domenica 30 marzo 2014

Recharging and reusing Acer laptop batteries on DIY projects with arduino

It's not unusual to end up with dead laptops whose battery is still useable for some other projects, the most usual practice, is to dismantle them to recover 18650 cells, but then you will have some prolems with integrating them on your project, the first one is avoiding stuff to catch fire or blow up and the second one is that you have no way to reliably tell how much % of charge is left on these batteries, and their real capacity ( capacity decreases with time and usage ).

These batteries in most cases are connected to an I2C/SMBus of the laptop and they use the Smart Battery protocol, which is a standard of most advanced battery powered systems, especially laptops.

The hardest part normally is to find the pinout of these batteries, but since Acer easily leaks complete motherboard schematics, it has not been hard to find these pinouts.

For now i've worked with a AL10B31 battery and a AS07B41 battery, they have different pinout, but both can be found on the respective schematics of the Quanta ZE6 and Quanta ZO3. One important thing to be careful about is that once you are using the connector from the motherboard outside the laptop, it's easy to insert it reversed by accident and cause serious damage both to the arduino and to the battery itself.


Charging the battery and using it

The AL10B31 and AS07B41 batteries are both 11.1V , that means that there are 3 cells in series, so to charge them you have to use a CC-CV power supply with 12.6V ( 4.2*3 ) and at most 1.5-2 amperes.
The battery of the Acer Aspire One ( Quanta ZE6 ) has an additional safety measure that will prevent you from charging it by just applying 12.6V on it's + and - terminals.
There's the pin 3 that has to be connected to ground with a 1KOhm resistor to enable the battery.
Quanta ZE6 battery connector
Even if in the above schematic it says "short", i've used 1KOhm resistor because that's the resistance i've measured on the netbook's motherboard between this pin and ground.

The AS07B41 that has same pinout of the AS07A(31/32/41) used by Quanta ZO3 but reversed and it does not have any safety measure that remove power from it's + and - pins if not connected to the laptop, so if you just want to charge it you are ready to go with 12.6V CC-CV and at most 2 amperes

Quanta ZO3 battery connector

Connecting to and monitoring the battery with an arduino

SMbus and I2C are physically compatible with each other, so like in the laptops i2c devices and battery share same bus, you can use the battery almost like and i2c device.
The only problem is that arduino Wire library does not give you much control over speed and start/stop, so, you will have to use a software i2c library.
The one i've used is http://playground.arduino.cc/Main/SoftwareI2CLibrary , this library allows both setting speed and having control on i2c start and stop.

Before including the .h of the library ( if you are too lazy to put the library in a folder, like me , you can just paste the contents on you sketch ), you have to define which pins to use for software I2C.


#define SDA_PIN 3 
#define SDA_PORT PORTD 
#define SCL_PIN 2
#define SCL_PORT PORTD
#define I2C_SLOWMODE 1


On the arduino mega 2560 these are pins 19 for SCL ( MBCLK ) and pin 18 for SDA ( MBDATA ) .
If you are using another type of arduino or you want to use different pins use https://spreadsheets.google.com/pub?key=rtHw_R6eVL140KS9_G8GPkA&gid=0 to find out what AVR port and pin number to use on the #define

You have also to define I2C_SLOWMODE in the case you have problems with communicating to the battery.

I'm using that sketch to read values from the battery:

LiquidCrystal lcd(12,11,50,51,52,53);
byte deviceAddress = 11;


#define VOLTAGE 0x09
#define TEMPERATURE 0x08
#define CURRENT 0x0a
#define CAPACITY 0x10
#define TIME_TO_FULL 0x13
#define CHARGE 0x0d
void setup()
{
  lcd.begin(16,2);
  //pinMode(22,INPUT_PULLUP);
  //pinMode(23,INPUT_PULLUP);
  
  Serial.begin(115200);  // start serial for output
  Serial.println(i2c_init());
  //pinMode(22,INPUT_PULLUP);
  //pinMode(23,INPUT_PULLUP);
  
  scan();
}
 int fetchWord(byte func)
{
  i2c_start(deviceAddress<<1 | I2C_WRITE);
  i2c_write(func);
  i2c_rep_start(deviceAddress<<1 | I2C_READ);

  

  byte b1 = i2c_read(false);
  byte b2 = i2c_read(true);
  i2c_stop();
  return (int)b1|((( int)b2)<<8);
}
void scan()
{
  byte i = 0;
  for ( i= 0; i < 127; i++  )
  {
     Serial.print("Address ");Serial.print(i);
    bool ack = i2c_start(i<<1 | I2C_WRITE);  
    if ( ack )
      Serial.println("OK");
    else
      Serial.println("NO");
    i2c_stop();
  }
}
void loop()
{
  int v = fetchWord(VOLTAGE);
  Serial.print("Voltage: ");
  Serial.println(v);
  lcd.clear();
  lcd.print((float)v/1000.0);lcd.print("V ");
  Serial.print("Temp: ");
  unsigned int tempk = fetchWord(TEMPERATURE);
  float tempc = ((float)tempk)/10.0-273.15;
  lcd.print(tempc);
  lcd.print("C");
  lcd.setCursor(0,1);
  Serial.println(tempc);
  Serial.print("Current (mA):" );
  int ma = fetchWord(CURRENT);
  lcd.print(ma);lcd.print("mA ");
  Serial.println(ma);
  Serial.print("Capacity (mAh):" );
  int mah = fetchWord(CAPACITY);
  Serial.println(mah);
  int ch = fetchWord(CHARGE);
  Serial.print("Charge PCT: ");Serial.print(ch);
  lcd.print(ch);lcd.print("% ");lcd.print(float(mah)/1000.0);lcd.print("Ah");
  Serial.print(" Minutes remaining for full charge: ");
  Serial.println(fetchWord(TIME_TO_FULL));
  delay(5000);
}

I've omitted the beginning because it's just the software i2c library and some includes like LiquidCrystal.h .

Quanta ZE6 battery


Charging AS07B41

Charging AS07A(31,32,41) , Quanta ZO3

Charging AS07B31 ( Same pinout as AS07B41 )
On the hardware side instead, you have to connect the arduino ground to the battery ground ( BE VERY SURE IT IS THE BATTERY GROUND ) and SCL, SDA pins respectively to MBCLK and MBDATA pins.
You have also, as the master of the i2c bus provide the pullup, so you have to use 2 10K resistors connected between +5V and the SDA,SCL lines.

Once you enable the battery ( if required ) , you should be able to communicate with it using that code.
If you are interested on monitoring other parameters, like remaining time to 0%, design voltage , manufacture date , etc, you can see what is the ID to use here http://sbs-forum.org/specs/sbdat110.pdf

Finally, i remind to who is going to use these batteries, that they have high energy density, so they can start a fire or explode if mishandled.

Edit: I've discovered that sometimes reading are wrong, lowering I2C frequency solves that issue , to do that modify

#if I2C_SLOWMODE
#define I2C_DELAY_COUNTER (((I2C_CPUFREQ/25000L)/2-19)/3)
#else

to

#if I2C_SLOWMODE
#define I2C_DELAY_COUNTER (((I2C_CPUFREQ/15000L)/2-19)/3)
#else



sabato 29 marzo 2014

Ricaricare una batteria di Acer Aspire One e monitorarne i parametri con arduino

Capita spesso di ritrovarsi delle batterie di portatili defunti, di cui non si sa cosa farne, la cosa più semplice è spaccarla e recuperarci le celle 18650, ma se poi ci si vuole realizzare qualche progetto, sorge subito il problema di come sapere quanta % di carica ha, il tempo di ricarica, e soprattutto avere un circuito di protezione che eviti esplosioni o eventi del genere.

La prima cosa fondamentale da sapere è che la quasi totalità di queste batterie utilizza SMBus ( Simile a I2C ) ed il protocollo Smart Battery, il quale è uno standard per monitorare i parametri delle batterie nei sistemi elettronici.

Nel nostro caso lavoreremo su una batteria AL10B31.
Fortunatamente del portatile di provenienza è stato possibile reperire lo schema elettrico e quindi avere il pinout della batteria

Ricarica e utilizzo della batteria


La prima cosa interessante è il pin 3, in questo schema è collegato direttamente a massa, ma sulla scheda madre associata alla batteria in mio possesso , invece ho visto che era presente 1kOhm fra questo pin e la massa, quindi ho provveduto a collegare tale resistenza fra il pin 3 e l'8.
È necessario prestare attenzione al fatto che una volta collegato questo pin a massa tramite la resistenza da 1K , è presente la tensione della batteria sui terminali , quindi bisogna evitare di creare cortocircuiti di qualsiasi genere , in quanto , si , probabilmente sono protette anche da corto queste batterie, ma se i cavi dovessero essere sottili invece di andare in protezione, possono ustionarvi le mani.

Per iniziare la ricarica, si possono applicare 12.6 volt fra il pin 8(-) e il pin 1(+) da un alimentatore con limitazione di corrente minore di 1.5-2 ampere.
Il fatto della limitazione di corrente e del voltaggio sono cose ESSENZIALI, pena nel peggior dei casi, incendi o esplosioni.


Collegamento con arduino


Per il collegamento con arduino le cose sono leggermente più complesse invece, ma completamente fattibili.
Per poter dialogare con SMBus, è necessario usare una libreria software per I2C reperibile qui http://playground.arduino.cc/Main/SoftwareI2CLibrary .
Una volta creato lo sketch arduino , prima di includere la libreria, è necessario impostare alcuni parametri

SDA_PIN , SDA_PORT , SCL_PIN , SCL_PORT

Nel mio caso utilizzando un arduino mega 2560, ho utilizzato


#define SDA_PIN 3 
#define SDA_PORT PORTD 
#define SCL_PIN 2
#define SCL_PORT PORTD
#define I2C_SLOWMODE 1

Questi parametri corrispondono all'utilizzare il pin 19 per SCL ed il pin 18 per SDA , nel caso si utilizzi un arduino diverso qui https://spreadsheets.google.com/pub?key=rtHw_R6eVL140KS9_G8GPkA&gid=0 è possibile consultare la mappatura delle porte AVR con i pin arduino.

Lo sketch che utilizzo per monitorare i parametri è il seguente
byte deviceAddress = 11;


#define VOLTAGE 0x09
#define TEMPERATURE 0x08
#define CURRENT 0x0a
#define CAPACITY 0x10
#define TIME_TO_FULL 0x13
#define CHARGE 0x0d
void setup()
{
  
  pinMode(22,INPUT_PULLUP);
  pinMode(23,INPUT_PULLUP);
  
  Serial.begin(115200);  // start serial for output
  Serial.println(i2c_init());
  pinMode(22,INPUT_PULLUP);
  pinMode(23,INPUT_PULLUP);
  
  scan();
}
unsigned int fetchWord(byte func)
{
  i2c_start(deviceAddress<<1 | I2C_WRITE);
  i2c_write(func);
  i2c_rep_start(deviceAddress<<1 | I2C_READ);

  

  byte b1 = i2c_read(false);
  byte b2 = i2c_read(true);
  i2c_stop();
  return (unsigned int)b1|(((unsigned int)b2)<<8);
}
void scan()
{
  byte i = 0;
  for ( i= 0; i < 127; i++  )
  {
     Serial.print("Indirizzo ");Serial.print(i);
    bool ack = i2c_start(i<<1 | I2C_WRITE);  
    if ( ack )
      Serial.println("OK");
    else
      Serial.println("NO");
    i2c_stop();
  }
}
void loop()
{
  
  Serial.print("Voltaggio: ");
  Serial.println(fetchWord(VOLTAGE));
  Serial.print("Temperatura: ");
  unsigned int tempk = fetchWord(TEMPERATURE);
  float tempc = ((float)tempk)/10.0-273.15;
  Serial.println(tempc);
  Serial.print("Corrente (mA):" );
  Serial.println(fetchWord(CURRENT));
  Serial.print("Capacità (mAh):" );
  Serial.println(fetchWord(CAPACITY));
  Serial.print("Carica: ");Serial.print(fetchWord(CHARGE));
  Serial.print(" Minuti rimanenti alla carica completa: ");
  Serial.println(fetchWord(TIME_TO_FULL));
  delay(5000);
}

Questo sketch all'avvio fa una scansione del bus per trovare le periferiche e poi di default si collega alla batteria che si trova all'indirizzo 0x11.

A livello hardware, per eseguire il collegamento, è necessario collegare 2 resistenze di pullup da 10K  verso +5v sui pin SCL e SDA e collegare MBDATA e MBCLK rispettivamente con SDA ed SCL su arduino.

Per poter monitorare altri parametri, è possibile consultare la documentazione di smart battery all'indirizzo http://sbs-forum.org/specs/sbdat110.pdf

Ricordo di evitare di caricare batterie al litio in modo sperimentale senza essere presenti durante la carica.

sabato 1 febbraio 2014

Routing da, alla rete di provenienza con Linux

Molto spesso può capitare di voler fare il forwarding dalla LAN alla LAN, ovvero avere un router che accetta i pacchetti in ingresso, e li reimmette sulla stessa rete magari eseguendoci sopra il load balancing o traffic shaping, ad esempio potrei avere una lan composta da un router alice, un router fastweb ed un server su cui ho a disposizione una sola interfaccia ethernet, ad occhio un sistemista non smanettone vi direbbe subito che dovete mettere altre 2 interfacce di rete sul server o non si può fare, FALSO.

Ora tralasciando la parte di traffic shaping e network balancing che è adeguatamente documentata altrove, vediamo come è possibile la cosa a livello teorico


Prendiamo in esempio la rete 192.168.0.0/24
Mettiamo che il server dove volete fare routing sia configurato per avere gateway predefinito 192.168.0.1 (Il router alice ) e che il server abbia indirizzo 192.168.0.2, teoricamente se io da una macchina con configurato gatweay 192.168.0.2, invio un pacchetto che ha come indirizzo di destinazione ad esempio 8.8.4.4 e come mac address di destinazione quello del server 192.168.0.2, il server lo riceverà, gli cambia di nuovo mac address di destinazione con quello del suo gateway e lo rispedisce in LAN di nuovo.
Se proviamo a farlo praticamente ci accorgiamo di una cosa però...

tiziano@tiziano-VPCF12Z1E ~ $ ping 8.8.4.4
PING 8.8.4.4 (8.8.4.4) 56(84) bytes of data.
64 bytes from 8.8.4.4: icmp_seq=1 ttl=46 time=99.5 ms
From 192.168.0.2: icmp_seq=2 Redirect Host(New nexthop: 192.168.0.1)
From 192.168.0.2 icmp_seq=2 Redirect Host64 bytes from 8.8.4.4: icmp_seq=2 ttl=46 time=71.9 ms
64 bytes from 8.8.4.4: icmp_seq=3 ttl=46 time=115 ms







Quello che sta succedendo in pratica è che il server sta dicendo al client che deve contattare direttamente 192.168.0.1 invece di lui, e questa cosa non va assolutamente bene perché se andiamo a fare un traceroute ci accorgiamo che il nostro server che dovrebbe fare traffic shaping oppure load balancing è stato bypassato, il traceroute partirà direttamente dal router di alice.

Questo problema si risolve disabilitando l'invio di pacchetti ICMP Redirect Host, utilizzando
sysctl -w net.ipv4.conf.all.send_redirects=0
E per ogni interfaccia
sysctl -w net.ipv4.conf.INTERFACCIA.send_redirects=0


http://www.cisco.com/en/US/tech/tk365/technologies_tech_note09186a0080094702.shtml Qui è documentato l'utilizzo del pacchetto ICMP Redirect in modo dettagliato

Una volta risolto questo problema al quale non sempre è scontata la soluzione dato che sono pochi ad avere setup funzionanti del genere, il resto della configurazione è praticamente uguale ad un normale routing, ovviamente quando si lavora sulle regole iptables , essendo l'interfaccia di ingresso ed uscita la stessa , si deve lavorare sugli indirizzi per distinguere i pacchetti