home *** CD-ROM | disk | FTP | other *** search
/ Hacks & Cracks / Hacks_and_Cracks.iso / hackersclub / km / library / cracking / fravia-taskman2.txt < prev    next >
Text File  |  1998-03-25  |  32KB  |  722 lines

  1. How To Disassemble A Windows Program
  2.  
  3.      After we've found and analyzed WinMain() (-> lesson 1), the next
  4.      places to inspect when you crack a program are the windows procedures
  5.      and dialog procedures (this is true only for Windows *programs*; for
  6.      DLL, on the countrary, the cracking procedures are different and the
  7.      relvant techniques will be discussed in another lesson).
  8.  
  9.      These WndProcs and DialogProcs are "callback" procedures: they are
  10.      *exported* from Windows executables, almost as the program were a DLL,
  11.      so that Windows can call them.
  12.  
  13.      And -hear, hear!- beacuse they are exported these crucial procedures
  14.      have *names* (almost always useful) that are accessible to any decent
  15.      Windows disassembler. In Taskman.lst, for example, WCB clearly
  16.      identifies TASKMANDLGPROC:
  17.  
  18.      Exported names by location:
  19.        1:007B     1 TASKMANDLGPROC      <- It's a DialogProc !
  20.  
  21.      It works out well that the WndProcs and DialogProcs show up so nicely
  22.      in the disassembled listings, because, as we know from Windows
  23.      programming, these subroutines are "where the action is" in event
  24.      driven Windows applications... or at least where the action begins.
  25.  
  26.      Furthermore we know that these subroutines will be most likely little
  27.      more than (possibly very large) message handling switch/case
  28.      statements. These usually look something like this: long FAR PASCAL
  29.      _export WndProc(HWND hWnd, WORD message, WORD wParam, LONG lPAram)
  30.  
  31.      long FAR PASCAL _export WndProc(HWND hWnd, WORD message, WORD
  32.      wParam, LONG lPAram)
  33.       { ...
  34.           switch (message)
  35.            {
  36.                case WM_CREATE:
  37.                  //... handle WM_CREATE message
  38.                  break;
  39.  
  40.                case WM_COMMAND:
  41.                  //... handle WM_COMMAND message
  42.                  break;
  43.                default:
  44.                  return DefWindowProc(hwnd, message, wParam, lParam);
  45.            }
  46.        }
  47.  
  48.      Wow! Yes! As you already guessed this means that... that we get
  49.      immediately 4 parameters for EACH exported WndProc or DlgProc!
  50.  
  51.      Actually there's no rule that states that a Windows WndProc or DlgProc
  52.      has to look like this... it's just that they almost always do!
  53.  
  54.      Here is how the parameters to the WndProc or DialogProc will appear in
  55.      the assembly language listing (after the function prologue):
  56.  
  57.      long FAR PASCAL _export WndOrDialogProc(HWND hwnd, WORD
  58.           message, WORD wParam, LONG lParam);
  59.  
  60.         lParam          = dword ptr [bp+6]
  61.         wParam          = word ptr [bp+0Ah]
  62.         message         = word ptr [bp+0Ch]
  63.         hWnd or hWndDlg = word ptr [bp+0Eh]
  64.  
  65.      With this knowledge, we can replace an otherwise meaningless [bp+0Ch]
  66.      with a label such as "message", a [bp+0Eh] with a "hwnd" or "hwndDlg",
  67.      and so on in *ANY* DialogProc and WndProc in *ANY* Windows program.
  68.  
  69.      The boilerplate nature of Windows programming greatly simplifies
  70.      cracking. For example, here is part of our Taskman exported:
  71.  
  72.      The problem here, of course, is what to make of all these magic
  73.      numbers: 0064, OO1C, 00F4 and so on... how are we going to figure out
  74.      what these mean?
  75.  
  76.      DialogProc: TASKMANDLGPROC:
  77.  
  78.        1.007B                           ;  TASKMANDLGPROC
  79.        ... (function prologue)
  80.        1.008A  8B760E           mov     si,  hWndDlg        ;[bp+0E]
  81.        1.008D  56               push    si
  82.        1.008E  6A64             push    0064
  83.  
  84.        1.0090  9AFFFF0000       call    USER.GETDLGITEM
  85.        1.0095  8BF8             mov     di, ax
  86.        1.0097  8B460C           mov     ax, message         ;[bp+0C]
  87.        1.009A  2D1C00           sub     ax, 001C
  88.        1.009D  7416             je      00B5
  89.        1.009F  2DF400           sub     ax, 00F4
  90.        1.00A2  7436             je      00DA
  91.        1.00A4  48               dec     ax
  92.        1.00A5  7503             jne     00AA
  93.        1.00A7  E98301           jmp     022D
  94.  
  95.        1.00AA >2D5303           sub     ax, 0353
  96.        1.00AD  7503             jne     00B2
  97.        1.00AF  E9D602           jmp     0388
  98.  
  99.        1.00B2 >E9C801           jmp     027D
  100.  
  101.        1.00B5 >837E0A00         cmp     word ptr wParam, 0  ;[bp+0A]
  102.        1.00B9  7403             je      00BE
  103.        1.00BB  E9BF01           jmp     027D
  104.        ...
  105.  
  106.      When examined via disassembled listings, Windows programs tend to
  107.      contain a lot of "magic numbers". Of course the actual source code
  108.      would be :
  109.  
  110.      *    #include '<'windows.h'>'          and
  111.      *    #define numeric constants for the various resources (menus,
  112.           strings, dialog controls, etc.) that it uses.
  113.  
  114.      Given a disassembled listing, it should be possible to turn a lot of
  115.      these seemingly senseless numbers back into something understandable.
  116.  
  117.      Let's start with the number 001C in TaskManDlgProc():
  118.  
  119.        1.0097  8B460C           mov     ax, message    ;[bp+0C]
  120.        1.009A  2D1C00           sub     ax, 001C
  121.        1.009D  7416             je      00B5
  122.  
  123.      If AX holds the *message* parameter to TaskManDlgProc() (line
  124.      1.0097)... then the value 001C must be a Windows WM_ message number
  125.      (one of those you can breakpoint to with WINICE's BMSG command, by the
  126.      way). Looking in WINDOWS.H, we find that 0x1C is WM_ACTIVATEAPP.
  127.  
  128.      TaskManDlgProc() is subtracting this value from AX and then jumping
  129.      somewhere (let's call it ON_ACTIVATEAPP) if the result is zero... i.e.
  130.      if it is WM_ACTIVATEAPP.
  131.  
  132.      This is an odd way to test whether (message == WM_ACTIVATEAPP): if the
  133.      test fails, and we do not take the jump to ON_ACTIVATEAPP, the message
  134.      number has 1C subtracted from it... and this value must be taken
  135.      account of by the next switch statement:
  136.  
  137.        1.009F  2DF400    sub    ax, 00F4 ; (+1C=110=WM_INITDIALOG)
  138.        1.00A2  7436      je     00DA     ; jump to ON_INITDIALOG
  139.        1.00A4  48        dec    ax       ; (110+1=111=WM_COMMAND)
  140.        1.00A5  7503      jne    00AA     ; no, go elsewhere
  141.        1.00A7  E98301    jmp    022D     ; yes, jump to ON_COMMAND
  142.  
  143.      Other WndProcs & DialogProcs will contain straightforward tests,
  144.      rather than testing via subtraction... is a matter of compiler choice.
  145.      In any case, a WndProc or DialogProc generally contains a collection
  146.      of handlers for different messages.
  147.  
  148.      In the case of TaskManDlgProc(), we can see that's handling
  149.      WM_ACTIVATEAPP, WM_INITDIALOG and WM_COMMAND. By itself, this
  150.      information is rather boring... however, it tells us what is happening
  151.      *elsewhere* in the function: 1.00B5 must be handling WM_ACTIVATEAPP
  152.      messages (therefore let's call it ON_ACTIVATEAPP), 1.00DA must be
  153.      handling WM_INITDIALOG, and 1.022D must be handling WM_COMMAND
  154.      messages.
  155.  
  156.      Write it down! This same basic technique -find where the [bp+0Ch]
  157.      "message" parameter to the WndProc or DialogProc is being rested, and
  158.      from that identify the locations that handle various messages- can be
  159.      used in *ANY* Windows program.
  160.  
  161.      Because handling messages is mostly what Windows applications do, once
  162.      we know where the message handling is, we pretty much can have our way
  163.      with the disassembled listing.
  164.  
  165.      Let's look now at TaskManDlgProc():
  166.  
  167.      TASKMANDLGPROC proc far
  168.        ...
  169.       DISPATCH_ON_MSG:
  170.        1.0097  8B460C    mov  ax, message        ;[bp+0C]
  171.        1.009A  2D1C00    sub  ax, WM_ACTIVATEAPP ;001C
  172.        1.009D  7416      je   ON_ACTIVATEAPP
  173.        1.009F  2DF400    sub  ax, 00F4 ; (+1C=110=WM_INITDIALOG)
  174.        1.00A2  7436      je   ON_INITDIALOG
  175.        1.00A4  48        dec  ax       ;(110+1=111=WM_COMMAND)
  176.        1.00A5  7503      jne  DEFAULT
  177.        1.00A7  E98301    jmp  ON_COMMAND
  178.       DEFAULT:
  179.        1.00AA >2D5303    sub  ax, 0353 ;(111+353=464=WM_USER+64
  180.        1.00AD  7503      jne ON_PRIVATEMSG ;00B2= some private msg
  181.        1.00AF  E9D602    jmp  0388
  182.       ON_PRIVATEMSG:
  183.        1.00B2 >E9C801    jmp  027D
  184.       ON_ACTIVATEAPP:
  185.        1.00B5 >837E0A00  cmp     word ptr wParam, 0  ;[bp+0A]
  186.        ...               ; code to handle WM_ACTIVATEAPP
  187.       ON_INITDIALOG:
  188.        ...               ; code to handle WM_INITDIALOG
  189.       ON_COMMAND:
  190.        ...               ; code to handle WM_COMMAND
  191.        1.022D >8B460A        mov   ax, wParam  ;[bp+0A]
  192.        1.0230  3D6800        cmp   ax, 0068    ; ? What's this ?
  193.        1.0233  7503          jne   0238
  194.        1.0235  E93301        jmp   036B
  195.        ...
  196.  
  197.      This is starting to look pretty reasonable. In particular, once we
  198.      know where WM_COMMAND is being handled, we are well on the way to
  199.      understand what the application does.
  200.  
  201.      WM_COMMAND is *very* important for understanding an application
  202.      behavior because the handler for WM_COMMAND is where it deals with
  203.      user commands such as Menu selections and dialog push button clicks...
  204.      a lot of what makes an application unique.
  205.  
  206.      If you click on "Cascade" in Task manager, for instance, it comes as a
  207.      WM_COMMAND, the same occurs if you click on "Tile" or "Switch To" or
  208.      "End Task".
  209.  
  210.      An application can tell which command a user has given it by looking
  211.      in the wParam parameter to the WM_COMMAND message.
  212.  
  213.      This is what we started to see at the ned of the TaskManDlgProc()
  214.      exerpt:
  215.  
  216.        ; We are handling WM_COMMAND, therefore wParam is here idItem,
  217.        ; i.e. a control or menu item identifier
  218.        1.022D >8B460A  mov  ax, wParam ;[bp+0A]
  219.        1.0230  3D6800  cmp  ax, 0068   ;ID number for a dialog control
  220.        1.0233  7503    jne   0238
  221.        1.0235  E93301  jmp   036B
  222.  
  223.        1.0238 >7603    jbe   023D
  224.        1.023A  E96001  jmp   039D
  225.  
  226.        1.023D >FEC8    dec   al        ;1
  227.        1.023F  7420    je    0261      ;if wParam==1 goto 1.0261
  228.        1.0241  FEC8    dec   al        ;1+1=2
  229.        1.0243  7503    jne   0248
  230.        1.0245  E94701  jmp   038F      ;if wParam==2 goto 1.038F
  231.  
  232.        1.0248 >2C62    sub   al, 62    ;2+62=64
  233.        1.024A  742A    je    0276
  234.        1.024C  FEC8    dec   al        ;64+1=65
  235.        1.024E  7432    je    0282
  236.        1.0250  2C01    sub   al, 01    ;65+1=66
  237.        1.0252  7303    jnb   0257
  238.        1.0254  E94601  jmp   039D
  239.  
  240.        1.0257 >2C01    sub   al, 01    ;66+1=67
  241.        1.0259  7703    ja    025E
  242.        1.025B  E9D200  jmp   0330
  243.  
  244.      It's clear that wParam is being compared (in an odd subtraction way)
  245.      to valus 1,2,65,66 and 67. What's going on?
  246.  
  247.      The values 1 and 2 are standard dialog button IDs:
  248.  
  249.      #define IDOK            1
  250.      #define IDCANCEL        2
  251.  
  252.      Therefore we have here the two "classical" push buttons:
  253.  
  254.        1.023D >FEC8    dec   al        ; 1 = OK
  255.        1.023F  7420    je    ON_OK     ; If 1 goto 1.0261= ON_OK
  256.        1.0241  FEC8    dec   al        ; 1+1=2= CANCEL
  257.        1.0243  7503    jne   NOPE      ; goto neither OK nor CANCEL
  258.        1.0245  E94701  jmp   ON_CANCEL ; if 2 goto 1.038F= ON_CANCEL
  259.  
  260.      The numbers 65, 66 etc are specific to TaskManager however, we will
  261.      not find them inside WINDOWS.H... so there is no home to find the
  262.      names of the commands to which these magic number correspond, unless
  263.      we happen to have a debug version of the program true? NO! FALSE!
  264.  
  265.      One of the notable things about Windows is that remarkably little
  266.      information is lost or thrown away compiling the source code. These
  267.      magic numbers seem to correspond in some way to the different Task
  268.      Manager push buttons... it's pretty obvious that there must be a way
  269.      of having applications tell Windows what wParam they want sent when
  270.      one of their buttons is clicked or when one of their menu items is
  271.      selected.
  272.  
  273.      Applications almost always provide Windows with this information in
  274.      their resources (they could actually define menus and controls
  275.      dynamycally, on the fly, but few applications take advantage of this).
  276.      These resources are part of the NE executable and are available for
  277.      our merry snooping around.
  278.  
  279.      This inspections of the resources in an EXE file is carried out by
  280.      means of special utilities, like RESDUMP, included with Windows source
  281.      (-> in my tool page). For example (I am using "-verbose" mode):
  282.  
  283.      DIALOG              10   (0Ah), "Task List"       [ 30, 22,160,107]
  284.      FONT "Helv"
  285.          LISTBOX         100  (64h), ""                [  3,  3,154, 63]
  286.          DEFPUSHBUTTON   1    (01h), "&Switch To"      [  1, 70, 45, 14]
  287.          PUSHBUTTON      101  (65h), "&End Task"       [ 52, 70, 45, 14]
  288.          PUSHBUTTON      2    (02h), "Cancel"          [103, 70, 55, 14]
  289.          STATIC          99   (63h), ""                [  0, 87,160,  1]
  290.          PUSHBUTTON      102  (66h), "&Cascade"        [  1, 90, 45, 14]
  291.          PUSHBUTTON      103  (67h), "&Tile"           [ 52, 90, 45, 14]
  292.          PUSHBUTTON      104  (68h), "&Arrange Icons"  [103, 90, 55, 14]
  293.  
  294.      YEAH! It's now apparent what the numbers 64h, 65h etc. mean. Imagine
  295.      you would write Taskmanager yourself... you would write something on
  296.      these lines:
  297.  
  298.           #define   IDD_SWITCHTO        IDOK
  299.           #define   IDD_TASKLIST        0x64
  300.           #define   IDD_ENDTASK         0x65
  301.           #define   IDD_CASCADE         0x66
  302.           #define   IDD_TILE            0x67
  303.           #define   IDD_ARRANGEICONS    0x68
  304.  
  305.      Let's look back at the last block of code... it makes now a lot more
  306.      sense:
  307.  
  308.      ON_COMMAND:
  309.        ; We are handling WM_COMMAND, therefore wParam is here idItem,
  310.        ; i.e. a control or menu item identifier
  311.       1.022D >8B460A  mov  ax, wParam   ;[bp+0A]
  312.       1.0230  3D6800  cmp  ax, 0068     ;is it the ID 68h?
  313.       ...
  314.       1.023D >FEC8    dec   al              ;1=IDOK=IDD_SWITCHTO
  315.       1.023F  7420    je    ON_SWITCHTO ;0261
  316.       1.0241  FEC8    dec   al          ;1+1=2=ID_CANCEL
  317.       1.0243  7503    jne   neither_OK_nor_CANCEL          ;0248
  318.       1.0245  E94701  jmp   ON_CANCEL   ;038F
  319.      neither_OK_nor_CANCEL:
  320.       1.0248 >2C62    sub   al, 62      ;2+62=64= IDD_TASKLIST
  321.       1.024A  742A    je    ON_TASKLIST ;0276
  322.       1.024C  FEC8    dec   al          ;64+1=65= IDD_ENDTASK
  323.       1.024E  7432    je    ON_ENDTASK  ;0282
  324.       1.0250  2C01    sub   al, 01      ;65+1=66= IDD_CASCADE
  325.       1.0252  7303    jnb   check_for_TILE             ;0257
  326.       1.0254  E94601  jmp   039D        ;something different
  327.      check_for_TILE:
  328.       1.0257 >2C01    sub   al, 01      ;66+1=67= IDD_TILE
  329.       1.0259  7703    ja    025E        ;it's something else
  330.       1.025B  E9D200  jmp   ON_TILE_or_CASCADE         ;0330
  331.  
  332.      In this way we have identified location 0330 as the place where
  333.      Taskman's "Cascade" and "Tile" buttons are handled... we have renaimed
  334.      it ON_TILE_or_CASCADE... let's examine its code and ensure it makes
  335.      sense:
  336.  
  337.      ON_TILE_or_CASCADE:
  338.       1.0330 >56             push  hwndDlg          ;si
  339.       1.0331  6A00           push  0000
  340.       1.0333  9A6F030000     call  USER.SHOWWINDOW
  341.  
  342.       1.0338  9A74030000     call  USER.GETDESKTOPWINDOW
  343.       1.033D  8BF8           mov   di, ax           ;hDesktopWnd
  344.       1.033F  837E0A66       cmp   word ptr wParam, 0066 ;IDD_CASCADE
  345.       1.0343  750A           jne   ON_TILE          ;034F
  346.       1.0345  57             push  di               ;hDesktopWnd
  347.       1.0346  6A00           push  0000
  348.       1.0348  9AFFFF0000     call  USER.CASCADECHILDWINDOWS
  349.       1.034D  EB2F           jmp   037E
  350.      ON_TILE:
  351.       1.034F >57             push  di
  352.       1.0350  6A10           push  0010
  353.       1.0352  9AFFFF0000     call  USER.GETKEYSTATE
  354.       1.0357  3D0080         cmp   ax, 8000
  355.       1.035A  7205           jb    0361
  356.       1.035C  B80100         mov   ax, 0001 ;1= MDITILE_HORIZONTAL
  357.       1.035F  EB02           jmp   0363
  358.  
  359.       1.0361 >2BC0           sub   ax, ax   ;0= MDITILE_VERTICAL
  360.  
  361.       1.0363 >50             push  ax
  362.       1.0364  9AFFFF0000     call  USER.TILECHILDWINDOWS
  363.       1.0369  EB13           jmp   037E
  364.  
  365.      Yes, it makes a lot of sense: We have found that the "Cascade" option
  366.      in Tile manager, after switching through the usual bunch of
  367.      switch/case loops, finally ends up calling an undocumented Windows API
  368.      function: CascadeChildWindows()... similarly, the "Tile" routine ends
  369.      up calling TileChildWindow().
  370.  
  371.      One thing screams for attention in the disassembled listing of
  372.      ON_TILE: the call to GetKeyState().
  373.  
  374.      As an example of the kind of information you should be able to gather
  375.      for each of these functions, if you are serious about cracking, I'll
  376.      give you now here, in extenso, the definition from H. Schildt's
  377.      "General purpose API functions", Osborne's Windows Programming Series,
  378.      Vol. 2, 1994 edition (I found both this valuable book and its
  379.      companion: volume 3: "Special purpose API functions", in a second hand
  380.      shop, in february 1996, costing the equivalent of a pizza and a
  381.      beer!). Besides this function is also at times important for our
  382.      cracking purposes, and represents therefore a good choice. Here the
  383.      description from pag.385:
  384.  
  385.           void GetKeyState(int iVirKey)
  386.  
  387.           Use GetKeyState() to determine the up, down or toggled status of
  388.           the specified virtual key. iVirKey identifies the virtual key. To
  389.           return the status of a standard alphanumeric character in the
  390.           range A-Z, a-z or 0-9, iVirKey must be set equal to its ANSI
  391.           ASCII value. All other key must use their related virtual key
  392.           codes. The function returns a value indicating the status of the
  393.           selected key. If the high-order bit of the byte entry is 1, the
  394.           virtual key is pressed (down); otherwise it is up. If you examine
  395.           a byte emlement's low-order bit and find it to be 1, the virtual
  396.           key has been toggled. A low-order bit of 0 indicates that the key
  397.           is untoggled.
  398.  
  399.           Under Windows NT/Win32, this function returns type SHORT.
  400.  
  401.           Usage:
  402.  
  403.           If your application needs to distinguish wich ALT, CTRL, or SHIFT
  404.           key (left or right) has been pressed, iVirKey can be set equal to
  405.           one of the following:
  406.  
  407.               VK_LMENU       VK_RMENU
  408.               VK_LCONTROL    VK_RCONTROL
  409.               VK_LSHIFT      VK_RSHIFT
  410.  
  411.           Setting iVirKey equal to VK_MENU, VK_CONTROL or VK_SHIFT
  412.           instructs GetKeyState() to ignore left and right, and only to
  413.           report back the status of teh virtual key category. This ability
  414.           to distinguish among virtual-key states is only available with
  415.           GetKeyState() and the related functions listed below.
  416.  
  417.           The following fragment obtains the state of the SHIFT key:
  418.  
  419.                if(GetKeyState(VK_SHIFT) {
  420.                                         ...
  421.                                         }
  422.  
  423.           Related Functions:
  424.  
  425.           GetAsyncKeyState(), GetKeyboardState(), MapVirtualKey(),
  426.           SetKeyboardState()
  427.  
  428.      Ok, let's go on... so we have in our code a "funny" call to
  429.      GetKeyState(). Because the Windows USer's Guide says nothing about
  430.      holding down a "state" (shift/ctrl/alt) key while selecting a button,
  431.      this sounds like another undocumented "goodie" hidden inside TASKMAN.
  432.  
  433.      Indeed, if you try it out on the 3.1 Taskman, you'll see that clicking
  434.      on the Tile button arranges all the windows on the desktop side by
  435.      side, but if you hold down the SHIFT key while clicking on the Tile
  436.      button, the windows are arranged in a stacked formation.
  437.  
  438.      To summarize, when the 3.1. Taskman Tile button is selected, the code
  439.      that runs in response looks like this:
  440.  
  441.      Tile:
  442.           ShowWindow(hWndDlg, SW_HIDE);      // hide TASKMAN
  443.           hDesktopWnd = GetDesktopWindow();
  444.           if (GetKeyState(VK_SHIFT) == 0x8000)
  445.                TileChildWindows(hDesktopWnd, MDITILE_HORIZONTAL);
  446.           else
  447.                TileChildWindows(hDesktopWnd, MDITILE_VERTICAL);
  448.  
  449.      Similarly, the CASCADE option in 3.1. TASKMAN runs the following code:
  450.  
  451.      Cascade:
  452.           ShowWindow(hWndDlg, SW_HIDE);      // hide TASKMAN
  453.           CAscadeChildWindows(GetDesktopWindow(), 0);
  454.  
  455.      We can then proceed through each TASKMAN option like this, rendering
  456.      the assembly language listing into more concise C.
  457.  
  458.      The first field to examine in TASKMAN is the Task List itself: how is
  459.      the "Task List" Listbox filled with the names of each running
  460.      application?
  461.  
  462.      What the List box clearly shows is a title bar for each visible top
  463.      level window, and the title bar is undoubtedly supplied with a call to
  464.      GetWindowText()... a function that obtains a copy of the specified
  465.      window handle's title.
  466.  
  467.      But how does TASKMAN enumerate all the top-level Windows? Taskman
  468.      exports TASKMANDLGPROC, but does not export any enumeration procedure.
  469.  
  470.      Most of the time Windows programs iterate through all existing windows
  471.      by calling EnumWindows(). Usually they pass to this function a pointer
  472.      to an application-supplied enumeration function, which therefore MUST
  473.      be exported. This callback function must have following prototype:
  474.  
  475.      BOOL CALLBACK EnumThreadCB(HWND hWnd, LPARAM lParam)
  476.  
  477.      Of course, the name a programmer chooses for such an exported function
  478.      is arbitrary. hWnd will receive the handle of each thread-associated
  479.      window.lParam receives lAppData, a 32-bit user- defined value. This
  480.      exported function must return non-zero to receive the next enumerated
  481.      thread-based window, or zero to stop the process.
  482.  
  483.      But here we DO NOT have something like TASKMANENUMPROC in the list of
  484.      exported functions... what's going on? Well... for a start TASKMAN IS
  485.      NOT calling EnumWindows()... Taskman uses a GetWindow() loop to fill
  486.      the "Task List" list box, study following C muster, sipping a good
  487.      cocktail and comparing it with the disassembled code you have printed:
  488.  
  489.      Task List:
  490.           listbox = GetDlgItem(hWndDlg, IDD_TASKLIST);
  491.           hwnd = GetWindow(hwndDlg, GW_HWNDFIRST);
  492.           while (hwnd)
  493.            {   if ((hwnd != hwndDlg) &&    //excludes self from list
  494.                   IsWindowVisible(hwnd) &&
  495.  
  496.                   GetWindow(hwnd, GW_OWNER))
  497.                {    char buf[0x50];
  498.                     GetWindowText(hwnd, buf, 0x50); // get titlebar
  499.                     SendMessage(listbox, LB_SETITEMDATA,
  500.                          SendMessage(listbox, LB_ADDSTRING, 0, buf),
  501.                          hwnd);         // store hwnd as data to go
  502.                }                        // with the titlebar string
  503.                hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  504.           }
  505.           SendMessage(lb, LB_SETCURSEL, 0, 0); // select first item
  506.  
  507.      The "End Task" opton in Taskman just sends a WM_CLOSE message to the
  508.      selected window, but only if it's not a DOS box. TASKMAN uses the
  509.      undocumented IsWinOldApTask() function, in combination with the
  510.      documented GetWindowTask() function, to determine if a given HWND
  511.      corresponds to a DOS box:
  512.  
  513.      End Task:
  514.        ...          // boring details omitted
  515.        if(IsWinOldApTask(GetWindowTask(hwndTarget)))
  516.          MaybeSwitchToSelecetedWindow(hwndTarget);
  517.  
  518.        if(IsWindow(hwndTarget) &&
  519.          (! (GetWindowLong(hwndTarget, GWL   5STYLE) & WS_DISABLED))
  520.        {
  521.           PostMessage(hwndTarget, WM_CLOSE, 0, 0);
  522.        }
  523.  
  524.      The "Arrange Icons" option simply runs the documented
  525.      ARrangeIconicWindows() function:
  526.  
  527.      Arrange Icons:
  528.        Showwindow(hWndDlg, SW_HIDE);
  529.        ArrangeIconiCWindows(GetDesktopWindow());
  530.  
  531.      The "Switch To" option in TASKMAN is also interesting. Like "Tile" and
  532.      "Cascade", this too it's just a user-interface covering an
  533.      undocupented Windows API function, in this case SwitchToThisWindow().
  534.  
  535.      Let's walk through the process of deciphering a COMPLETELY unlabelled
  536.      Windows disassembly listing, that will be most of the time your
  537.      starting situation when you crack, and let's turn it into a labelled C
  538.      code.
  539.  
  540.      By the way, there does exist an interesting school of research, that
  541.      attempts to produce an "EXE_TO_C" automatical converter. The only
  542.      cracked version of this program I am aware of is called E2C.EXE, is
  543.      198500 bytes long, has been developed in 1991 by "The Austin Code
  544.      Works and Polyglot International" in Jerusalem (Scott Guthery:
  545.      guthery@acw.com), and has been boldly brought to the cracking world by
  546.      Mithrandir/AlPhA/MeRCeNarY. Try to get a copy of this tool... it can
  547.      be rather interesting for our purposes ;-)
  548.  
  549.      Here is the raw WCB disassembled code for a subroutine within TASKMAN,
  550.      called from the IDD_SWITCHTO handling code in TaskManDlgProc():
  551.  
  552.       1.0010 >55             push    bp
  553.       1.0011  8BEC           mov     bp, sp
  554.       1.0013  57             push    di
  555.       1.0014  56             push    si
  556.       1.0015  FF7604         push    word ptr [bp+04]
  557.       1.0018  681A04         push    041A
  558.       1.001B  FF7604         push    word ptr [bp+04]
  559.       1.001E  680904         push    0409
  560.       1.0021  6A00           push    0000
  561.       1.0023  6A00           push    0000
  562.       1.0025  6A00           push    0000
  563.       1.0027  9A32000000     call    USER.SENDMESSAGE
  564.       1.002C  50             push    ax
  565.       1.002D  6A00           push    0000
  566.       1.002F  6A00           push    0000
  567.       1.0031  9AEF010000     call    USER.SENDMESSAGE
  568.       1.0036  8BF8           mov     di, ax
  569.       1.0038  57             push    di
  570.       1.0039  9A4C000000     call    USER.ISWINDOW
  571.       1.003E  0BC0           or      ax, ax
  572.       1.0040  742A           je      006C
  573.       1.0042  57             push    di
  574.       1.0043  9AFFFF0000     call    USER.GETLASTACTIVEPOPUP
  575.       1.0048  8BF0           mov     si, ax
  576.       1.004A  56             push    si
  577.       1.004B  9AA4020000     call    USER.ISWINDOW
  578.       1.0050  0BC0           or      ax, ax
  579.       1.0052  7418           je      006C
  580.       1.0054  56             push    si
  581.       1.0055  6AF0           push    FFF0
  582.       1.0057  9ACD020000     call    USER.GETWINDOWLONG
  583.       1.005C  F7C20008       test    dx, 0800
  584.       1.0060  750A           jne     006C
  585.       1.0062  56             push    si
  586.       1.0063  6A01           push    0001
  587.       1.0065  9AFFFF0000     call    USER.SWITCHTOTHISWINDOW
  588.       1.006A  EB07           jmp     0073
  589.  
  590.       1.006C >6A00           push    0000
  591.       1.006E  9ABC020000     call    USER.MESSAGEBEEP
  592.  
  593.       1.0073 >5E             pop     si
  594.       1.0074  5F             pop     di
  595.       1.0075  8BE5           mov     sp, bp
  596.       1.0077  5D             pop     bp
  597.       1.0078  C20200         ret     0002
  598.  
  599.      The RET 0002 at the end tells us that this is a near Pascal function
  600.      that expects one WORD parameter, which appears as [bp+4] at the top of
  601.      the code.
  602.  
  603.      Because [bp+4] is being used as the first parameter to SendMessage(),
  604.      it must be an HWND of some sort.
  605.  
  606.      Here is the muster for SendMessage(): LRESULT SendMessage(HWND hWnd,
  607.      UINT uMsg, WPARAM wMsgParam1, LPARAM lMsgParam2), where hWnd
  608.      identifies the Window receiving the message, uMsg identifies the
  609.      message being sent, wMsgParam1 & lMsgParam2 contain 16 bits and 32
  610.      bits of message-specific information.
  611.  
  612.      Finally, we don't see anything being moved into AX or DX near the end
  613.      of the function, so it looks as if this function has no return value:
  614.  
  615.      void near pascal some_func(HWND hwnd)
  616.  
  617.      Let's look once more at it... the function starts off with two nested
  618.      calls to SendMessage (using the message numbers 41Ah and 409h). These
  619.      numbers are greater than 400h, they must therefore be WM_USER+XX
  620.      values. Windows controls such as edit, list and combo boxes all use
  621.      WM_USER+XX notification codes.
  622.  
  623.      The only appropriate control in TASKMAN is the list box, so we can
  624.      just look at the list of LB_XXX codes in WINDOWS.H. 1Ah is 26 decimal,
  625.      therefore 41Ah is WM_USER+26, or LB_GETITEMDATA. Let's see what
  626.      Osborne's "Special Purpose API functions" says about it (pag.752):
  627.  
  628.      LB_GETITEMDATA
  629.       When sent: To return the value associated with a list-box item.
  630.       wParam:    Contains the index to the item in question
  631.       lParam:    Not used, must be 0
  632.       Returns:   The 32-bit value associated with the item
  633.  
  634.      Similarly, 409h is WM_USER+9, which in the case of a list box means
  635.      LB_GETCURSEL. We saw earlier that TASKMAN uses LB_SETITEMDATA to store
  636.      each window title's associated HWND. LB_GETITEMDATA will now retrive
  637.      this hwnd:
  638.  
  639.        hwnd = SendMessage(listbox, LB_GETITEMDATA,
  640.           SendMessage(listbox, LB_GETCURSEL, 0, 0), 0);
  641.  
  642.      Notice that now we are caling the parameter to some_func() a listbox,
  643.      and that the return value from LB_GETITEMDATA is an HWND.
  644.  
  645.      How would we know it's an hwnd without our references? We can see the
  646.      LB_GETITEMDATA return value (in DI) immediatly being passed to
  647.      IsWindow() at line 1.0039:
  648.  
  649.       ; IsWindow(hwnd = SendMessage(...));
  650.      1.0031 9AEF010000   call far ptr SENDMESSAGE
  651.      1.0036 8BF8         mov  di, ax
  652.      1.0038 57           push di
  653.      1.0039 9A4C000000   call far ptr ISWINDOW
  654.  
  655.      Next, the hwnd is passed to GetLastActivePopup(), and the HWND that
  656.      GetLastActivePopup() returns is then checked with IsWindow()...
  657.      IsWindow() returns non-zero if the specified hWnd is valid, and zero
  658.      if it is invalid:
  659.  
  660.       ; IsWindow(hwndPopup = GetLastActivePopup(hwnd));
  661.      1.0042  57          push    di
  662.      1.0043  9AFFFF0000  call    USER.GETLASTACTIVEPOPUP
  663.      1.0048  8BF0        mov     si, ax      ; save hwndPopup in SI
  664.      1.004A  56          push    si
  665.      1.004B  9AA4020000  call    USER.ISWINDOW
  666.  
  667.      Next, hwndPopup (in SI) is passed to GetWindowLong(), to get
  668.      informations about this window. Here is time to look at WINDOWS.H to
  669.      figure out what 0FFF0h at line 1.055 and 800h at line 1.005C are
  670.      supposed to mean:
  671.  
  672.       ; GetWindowLong(hwndPopup, GWL_STYLE) & WS_DISABLED
  673.      1.0054  56          push  si             ;hwndPopup
  674.      1.0055  6AF0        push  GWL   5STYLE   ;0FFF0h = -16
  675.      1.0057  9ACD020000  call  USER.GETWINDOWLONG
  676.      1.005C  F7C20008    test  dx, 0800       ;DX:AX= 800:0= WS_DISABLED
  677.  
  678.      Finally, as the whole point of this exercise, assuming this checked
  679.      window passes all its tests, its last active popup is switched to:
  680.  
  681.       ; SwitchToRhisWindow(hwndPopup, TRUE)
  682.      1.0062  56         push  si             ;hwndPopup
  683.  
  684.      1.0063  6A01       push  0001
  685.      1.0065  9AFFFF0000 call  USER.SWITCHTOTHISWINDOW
  686.  
  687.      It's here that all possible questions START: SwitchToThisWindow is not
  688.      documented... therefore we do not know the purpose of its second
  689.      parameter, apparently a BOOL. We cannot even tell why
  690.      SwitchToThisWindow() is being used... when SetActiveWindow(),
  691.      SetFocus() or BringWindowToTop() might do the trick. And why is the
  692.      last active popup and not the window switched to?
  693.  
  694.      But let's resume for now our unearthed mysterious function, that will
  695.      switch to the window selected in the Task List if the window meets all
  696.      the function's many preconditions:
  697.  
  698.      void MaybeSwitchToSelectedWindow(HWND listbox)
  699.      {
  700.        HWND hwnd, hwndPopup;
  701.        // first figure out wich window was selected in the Task List
  702.        if (IsWindow(hwnd = SendMessage(listbox, LB_GETITEMDATA,
  703.            SendMessage(listbox, LB_GETCURSEL, 0, 0), 0)))
  704.         {
  705.          if (IsWindow(hwndPopup = GetLastActivePopup(hwnd)))
  706.          {
  707.           if (! (GetWindowLong(hwndPopup, GWL_STYLE) & WS_DISABLED))
  708.           {
  709.              SwitchToThisWindow(hwndPopup, TRUE);
  710.              return;
  711.           }
  712.          }
  713.        MessageBeep(0);        //Still here... error!
  714.      }
  715.  
  716.      Now we have a good idea of what TASKMAN does (it sure took a long time
  717.      to understand those 3K bytes of code!). In the next lessons we'll use
  718.      what we have learned to crack together some common Windows programs.
  719.      (->lesson 3)
  720.  
  721.                                                                      FraVia
  722.