Hex-Rays v1.6 Comparison Page

Welcome to the Hex-Rays Decompiler v1.6 comparison page! Below you will find side-by-side comparisons of Hex-Rays v1.5 and v1.6 outputs. Please maximize the window too see both columns simultaneously. The previous comparison can be found here.

The following original exhibits are displayed on this page:

  1. Inline CFString constants
  2. More humanly if-conditions
  3. Support for CONTAINING_RECORD macro
  4. Support for LIST_ENTRY macros
  5. Better tail call recognition
  6. Improved memset recognition
  7. Support for TEB/KPCR references
  8. Better char/short variable recognition
  9. Better recognition of inline functions
  10. Structure copying – 1
  11. Structure copying – 2
  12. Structure copying – 3
  13. Support for union fields
  14. Support for merged calls
Better recognition of inline functions
v27 = (char *)operator new(v24); v28 = v27; strcpy(v27, *v3); v29 = -1; v30 = ” “; do { if ( !v29 ) break; v12 = *v30++ == 0; —v29; } while ( !v12 ); v31 = ~v29; v32 = v31; v33 = &v30[-v31]; v34 = -1; v35 = v27; do { if ( !v34 ) break; v12 = *v35++ == 0; —v34; } while ( !v12 ); memcpy(v35 – 1, v33, v32); v36 = v3[1]; v37 = -1; do { if ( !v37 ) break; v12 = *v36++ == 0; —v37; } while ( !v12 ); v38 = ~v37; v39 = &v36[-v38]; v40 = v38; v41 = v27; v42 = -1; do { if ( !v42 ) break; v12 = *v41++ == 0; —v42; } while ( !v12 ); memcpy(v41 – 1, v39, v40); v43 = ” “; v44 = -1; do { if ( !v44 ) break; v12 = *v43++ == 0; —v44; } while ( !v12 ); v45 = ~v44; v46 = &v43[-v45]; v47 = v45; v48 = v27; v49 = -1; do { if ( !v49 ) break; v12 = *v48++ == 0; —v49; } while ( !v12 ); memcpy(v48 – 1, v46, v47); v50 = v3[2]; v51 = -1; do { if ( !v51 ) break; v12 = *v50++ == 0; —v51; } while ( !v12 ); v52 = ~v51; v53 = &v50[-v52]; v54 = v52; v55 = v27; v56 = -1; do { if ( !v56 ) break; v12 = *v55++ == 0; —v56; } while ( !v12 ); memcpy(v55 – 1, v53, v54); if ( v3[3] ) { argca = v3 + 3; v91 = v3 + 3; do { v57 = ” \””; v58 = -1; do { if ( !v58 ) break; v12 = *v57++ == 0; —v58; } while ( !v12 ); v59 = ~v58; v60 = &v57[-v59]; v61 = v59; v62 = v27; v63 = -1; do { if ( !v63 ) break; v12 = *v62++ == 0; —v63; } while ( !v12 ); memcpy(v62 – 1, v60, v61); v64 = *argca; v65 = -1; do { if ( !v65 ) break; v12 = *v64++ == 0; —v65; } while ( !v12 ); v66 = ~v65; v67 = &v64[-v66]; v68 = v66; v69 = v27; v70 = -1; do { if ( !v70 ) break; v12 = *v69++ == 0; —v70; } while ( !v12 ); memcpy(v69 – 1, v67, v68); if ( (*argca)[strlen(*argca) – 1] == 92 ) { v71 = “\\”; v72 = -1; do { if ( !v72 ) break; v12 = *v71++ == 0; —v72; } while ( !v12 ); v73 = ~v72; v74 = &v71[-v73]; v75 = v73; v76 = v27; v77 = -1; do { if ( !v77 ) break; v12 = *v76++ == 0; —v77; } while ( !v12 ); memcpy(v76 – 1, v74, v75); } v78 = “\””; v79 = -1; do { if ( !v79 ) break; v12 = *v78++ == 0; —v79; } while ( !v12 ); v80 = ~v79; v81 = &v78[-v80]; v82 = v80; v83 = v27; v84 = -1; do { if ( !v84 ) break; v12 = *v83++ == 0; —v84; } while ( !v12 ); memcpy(v83 – 1, v81, v82); ++v91; argca = v91; } while ( *v91 ); } v18 = (char *)operator new(v15); strcpy(v18, *v3); strcat(v18, ” “); strcat(v18, v3[1]); strcat(v18, ” “); strcat(v18, v3[2]); if ( v3[3] ) { argca = v3 + 3; v25 = v3 + 3; do { strcat(v18, ” \””); strcat(v18, *argca); if ( (*argca)[strlen(*argca) – 1] == 92 ) strcat(v18, “\\”); strcat(v18, “\””); ++v25; argca = v25; } while ( *v25 ); }
Sorry for a long sample, the previous version of the decompiler was not handling strlen() well enough. It is a never ending fight and perfection is impossible, but we still continue to work on it. Recognition of inline functions is an incredibly hard problem, but the new version has a better engine to recognize them. There is plenty of room for improvement, to put it mildly.
Support for union fields
PIO_STACK_LOCATION _stacklocation; // [sp+10h] [bp-14h]@1 if ( *&stacklocation->Parameters.Create.FileAttributes == 0x224010 ) { v8 = stacklocation->Parameters.Create.Options == 20; Semaphore = 0; if ( !v8 ) goto LABEL_18; if ( stacklocation->Parameters.Create.SecurityContext < 1 ) goto LABEL_87; v23 = Irp->AssociatedIrp.MasterIrp; v33 = &Semaphore; v32 = stacklocation->FileObject; memcpy(&v27, v23, 0x14u); DeviceObjecta = ChanMgrGetByHandleAndFileObject(v27, v28, v29, v30, v31, v32, &Semaphore); if ( DeviceObjecta < 0 ) goto LABEL_92; v24 = Irp->AssociatedIrp.MasterIrp; v33 = &v36; v32 = stacklocation->Parameters.Create.SecurityContext; v6 = ChannelIRead(Semaphore, v24, v32, &v36); LABEL_90: v33 = Semaphore; goto LABEL_91; } if ( *&stacklocation->Parameters.Create.FileAttributes == 2244628 ) { v8 = stacklocation->Parameters.Create.Options == 20; Semaphore = 0; if ( v8 && stacklocation->Parameters.Create.SecurityContext == 1 ) { v22 = Irp->AssociatedIrp.MasterIrp; PIO_STACK_LOCATION _stacklocation; // [sp+10h] [bp-14h]@1 if ( stacklocation->Parameters.DeviceIoControl.IoControlCode == 0x224010 ) { v8 = stacklocation->Parameters.Create.Options == 20; Semaphore = 0; if ( !v8 ) goto LABEL_18; if ( stacklocation->Parameters.Read.Length < 1 ) goto LABEL_87; buf = Irp->AssociatedIrp.SystemBuffer; v33 = &Semaphore; v32 = stacklocation->FileObject; memcpy(&v27, buf, 0x14u); DeviceObjecta = ChanMgrGetByHandleAndFileObject(v27, v28, v29, v30, v31, v32, &Semaphore); if ( DeviceObjecta < 0 ) goto LABEL_92; v24 = Irp->AssociatedIrp.SystemBuffer; v33 = &v36; v32 = stacklocation->Parameters.DeviceIoControl.OutputBufferLength; v6 = ChannelIRead(Semaphore, v24, v32, &v36); LABEL_90: v33 = Semaphore; goto LABEL_91; } if ( stacklocation->Parameters.DeviceIoControl.IoControlCode == 2244628 ) { v8 = stacklocation->Parameters.DeviceIoControl.InputBufferLength == 20; Semaphore = 0; if ( v8 && stacklocation->Parameters.DeviceIoControl.OutputBufferLength == 1 ) { v22 = Irp->AssociatedIrp.SystemBuffer;
Finally the decompiler has proper support for union fields. Previously analysing code with unions could quickly turn into a nightmare because the decompiler would just use the first union field and would not allow you to change it. The code, while it had the field names, was very misleading because these names could be completely wrong. This is what we have on the left sample.

The new version is much better in this aspect. First, it tries to determine the best union field using several heuristic rules. It checks the disassembly listing for ‘structure offset’ operands, checks the current context to select the best fit union field. In many cases no user intervention is required. However, if the decompiler fails to pick the corrent union field, the user can always correct it by selecting the desired union field manually. Even complex situations like a union with another nested union or structure are supported. Anonymous nested unions are represented correctly too.

Support for merged calls
INT_PTR __stdcall EditBinaryValueDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPHELPINFO hinfo) { LONG v5; // [email protected] HWND v6; // [sp-10h] [bp-14h]@10 const WCHAR *v7; // [sp-Ch] [bp-10h]@10 UINT v8; // [sp-8h] [bp-Ch]@10 ULONG_PTR v9; // [sp-4h] [bp-8h]@10 if ( uMsg == WM_HELP ) { v9 = (ULONG_PTR)s_EditBinaryValueHelpIDs; v8 = HELP_WM_HELP; v7 = g_pHelpFileName; v6 = hinfo->hItemHandle; goto LABEL_12; } if ( uMsg == WM_CONTEXTMENU ) { v9 = (ULONG_PTR)s_EditBinaryValueHelpIDs; v8 = HELP_CONTEXTMENU; v7 = g_pHelpFileName; v6 = (HWND)wParam; LABEL_12: WinHelpW(v6, v7, v8, v9); return 1; } if ( uMsg == WM_INITDIALOG ) return EditBinaryValue_OnInitDialog(hwndDlg, wParam, (LONG)hinfo); if ( uMsg != 273 || (signed int)(unsigned __int16)wParam <= 0 || (signed int)(unsigned __int16)wParam > 2 ) return 0; v5 = GetWindowLongW(hwndDlg, 8); *(_DWORD *)(v5 + 8) = dword_105A048; *(_DWORD *)(v5 + 4) = hMem; hMem = 0; EndDialog(hwndDlg, (unsigned __int16)wParam); return 1; } INT_PTR __stdcall EditBinaryValueDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPHELPINFO hinfo) { LONG v5; // [email protected] if ( uMsg == WM_HELP ) { WinHelpW(hinfo->hItemHandle, g_pHelpFileName, HELP_WM_HELP, (ULONG_PTR)s_EditBinaryValueHelpIDs); } else { if ( uMsg == WM_CONTEXTMENU ) { WinHelpW((HWND)wParam, g_pHelpFileName, HELP_CONTEXTMENU, (ULONG_PTR)s_EditBinaryValueHelpIDs); } else { if ( uMsg == WM_INITDIALOG ) return EditBinaryValue_OnInitDialog(hwndDlg, wParam, (LONG)hinfo); if ( uMsg != 273 || (signed int)(unsigned __int16)wParam <= 0 || (signed int)(unsigned __int16)wParam > 2 ) return 0; v5 = GetWindowLongW(hwndDlg, 8); *(_DWORD *)(v5 + 8) = dword_105A048; *(_DWORD *)(v5 + 4) = hMem; hMem = 0; EndDialog(hwndDlg, (unsigned __int16)wParam); } } return 1; }

NOTE: these are just some selected examples that can be illustrated as a side-by-side difference. Hex-Rays Decompiler v1.6 includes are many other improvements and new features that are not mentioned on this page – simply because there was nothing to compare them with. Also, some improvements have already been illustrated in the previous comparisons. Please refer to the news page for more details.

This is all for the moment. Please come back for more examples!