Page 1 of 2 12 LastLast
Results 1 to 15 of 19
  1. #1
    Ende!'s Avatar
    Join Date
    Feb 2010
    Gender
    male
    Posts
    33
    Reputation
    10
    Thanks
    33

    [IDC] Tiny script automatically naming ~300 functions by parsing debug strings

    Yo MPGH!

    I guess that most of you won't know me, as I wasn't very active over here in the past due to some private reasons, but I said to myself: "Why not giving the guys over there a shot by releasing some stuff and see what they do with it.".

    Beforehand: if you have no clue about how to use IDA and what the fuck I'm talking about: please don't tire me with silly questions.

    I started divin' into CF reverse engineering (with varying activity) about a month ago and the first thing I realized was that, in many cases, it's very easy to find out what the game's code is good for due to the nice "commenting" with debug strings used by the CrossFire developers literally everywhere. There's one function I use to call LogActionf (probably no perfect name as it actually does not log anything but pushes the given format string into CrossFire's debug console), that is used all over the code, probably prototyped somehow like this:
    Code:
    void LogActionf(const char *szFormat, ...);
    Furthermore, there is the following format string used in 543 cases
    Code:
    LT ERROR: %s returned %s (%s)
    where the first placeholder is filled with the calling function's name, the second one with some kind of error "descriptor" (e.g. LT_NOTFOUND) and the third one with a string describing the error. To this format string, in turn, a pointer is addressed which I labeled pLtErrorSReturn. Using this simple logic the script later in this post is able to rename a whole bunch of functions in the game to something better fitting than sub_XXXXXX.

    tl;dr:
    1. Locate format string (LT ERROR: %s returned %s (%s))
    2. Check X-Refs for a data reference (pointer to format-string)
    3. Take a look at the X-Refs to the pointer and see what function it is pushed to (will be LogActionf)
    4. Rename the format-string ptr to pLtErrorSReturn and the function it is pushed to to LogActionf
    5. Fire script and admire how it gives ~300 functions correct names


    Code:
    #include <idc.idc>
    
    static main()
    {
       auto  strAddr     = LocByName("pLtErrorSReturn"), 
             logFunAddr  = LocByName("LogActionf"), 
             curXref, 
             curAddr,
             curTraceAddr,
             firstParamFound,
             funcNameStrAddr,
             regId,
             funAddr,
             funName,
             oldFunName;
          
       for(curXref = DfirstB(strAddr)
          ; curXref != BADADDR
          ; curXref = DnextB(strAddr, curXref))
       {
          // locate function name
          // ------------------------------------------------
       
          // Check mnemonic
          if (GetMnem(curXref) != "mov" 
             || GetOpType(curXref, 0) != o_reg
             || GetOpType(curXref, 1) != o_mem)
          {
             Message("[0x%08X] referencing instruction is not \"mov eXx, [mem]\"!\n", curXref);
             continue;
          }
          
          // Find LogActionf call (max 50 bytes away)
          for (curAddr = curXref
             ; curAddr != BADADDR
             ; curAddr = NextAddr(curAddr))
          {
             // Is call to log fun? Break loop
             if (GetMnem(curAddr) == "call" 
                && GetOpnd(curAddr, 0) == "LogActionf")
             {
                break;
             }
             
             if (curAddr - curXref > 50)
             {
                Message("[0x%08X] too many steps till LogActionf call\n", curXref);
                return;
             }
          }
          
          // Find LogActionf's second parameter
          for (firstParamFound = 0; curAddr != BADADDR; curAddr = PrevAddr(curAddr))
          {
             // Conditional?
             if (RfirstB0(curAddr) != BADADDR)
             {
                Message("[0x%08X] conditional situation\n", curAddr);
                break;
             }
             
             // Desired parameter?
             if (GetMnem(curAddr) == "push")
             {
                if (!firstParamFound)
                   firstParamFound = 1;
                else
                {
                   funcNameStrAddr = 0;
                
                   // In case the operand is a register, we have to trace what
                   // is written into it
                   if (GetOpType(curAddr, 0) == o_reg)
                   {
                      regId = GetOperandValue(curAddr, 0);
                                        
                      for (curTraceAddr = curAddr
                         ; curTraceAddr != BADADDR
                         ; curTraceAddr = PrevAddr(curTraceAddr))
                      {
                         // Does instruction write into our register?
                         if (GetOpType(curTraceAddr, 0) == o_reg
                            && GetOperandValue(curTraceAddr, 0) == regId
                            && GetOpType(curTraceAddr, 1) == o_mem)
                         {
                            funcNameStrAddr = Dword(GetOperandValue(curTraceAddr, 1));
                            break;
                         }
                         
                         // Max. trace distance = 50 non-conditional bytes
                         if (curAddr - curTraceAddr > 50 || RfirstB0(curTraceAddr) != BADADDR)
                         {
                            Message("[0x%08X] conditional situation or trace width exceeded\n", 
                                    curTraceAddr);
                            break;
                         }
                      }
                   } 
                   else if (GetOpType(curAddr, 0) == o_imm)
                      funcNameStrAddr = GetOperandValue(curAddr, 0);
                   else
                   {
                      Message("[0x%08X] unsupported operand\n", curAddr);
                      return;
                   }
                   
                   // check if string contains spaces (filter out names with comments)
                   funName = 0;
                   if (funcNameStrAddr)
                   {
                      if (strstr(GetString(funcNameStrAddr, -1, ASCSTR_C), " ") != -1)
                      {
                         Message("[0x%08X] no valid function name\n", curAddr);
                         funcNameStrAddr = 0;
                      }
                      else
                         funName = GetString(funcNameStrAddr, -1, ASCSTR_C);
                   }
                      
                   break;
                }
             }
          }
          
          // rename function
          // ------------------------------------------------
          
          // only if function's name could be determined
          if (funName)
          {   
             oldFunName = GetFunctionName(curXref);
             
             if (oldFunName != "" && !hasName(GetFlags(LocByName(oldFunName))))
             {
                // make name unique
                while (LocByName(funName) != BADADDR)
                   funName = funName + "_";
             
                Message("[0x%08X] %s => %s\n", LocByName(oldFunName), oldFunName, funName);
                MakeNameEx(LocByName(oldFunName), funName, SN_CHECK | SN_NON_AUTO);
             }
          }
          
          // ------------------------------------------------
          
       }
       
       Message("Done, leaving.\n");
    } // ==> main
    Hope you have some use for that!

    Greets,
    Ende! - **** team

  2. The Following 6 Users Say Thank You to Ende! For This Useful Post:

    Code[VB] (07-06-2012),Fly3r (07-06-2012),giniyat101 (07-06-2012),mslol (07-06-2012),RemoteKontrol (07-06-2012),[mi5 (07-07-2012)

  3. #2
    RemoteKontrol's Avatar
    Join Date
    Jun 2012
    Gender
    male
    Posts
    18
    Reputation
    10
    Thanks
    56
    i Have question i pm you
    Last edited by RemoteKontrol; 07-06-2012 at 05:16 AM.

  4. #3
    giniyat101's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Location
    Not telling.
    Posts
    1,935
    Reputation
    130
    Thanks
    1,380
    My Mood
    Dead
    nice work.. i hope you make one for ollydbg too


     



    [img]https://i43.photobucke*****m/albums/e367/DeteSting/Steam-update.gif[/img]

  5. #4
    Ende!'s Avatar
    Join Date
    Feb 2010
    Gender
    male
    Posts
    33
    Reputation
    10
    Thanks
    33
    Quote Originally Posted by giniyat101 View Post
    nice work.. i hope you make one for ollydbg too
    Honestly I'm no big fan of OllyDbg in regards of reverse eningeering contexts - I prefer static analysis using IDA. So I'm sorry to tell you that this probably won't happen.

  6. The Following 2 Users Say Thank You to Ende! For This Useful Post:

    giniyat101 (07-07-2012),mslol (07-06-2012)

  7. #5
    BlackLite's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Posts
    547
    Reputation
    58
    Thanks
    1,035
    My Mood
    Aggressive
    Quote Originally Posted by Ende! View Post
    Yo MPGH!

    I guess that most of you won't know me, as I wasn't very active over here in the past due to some private reasons, but I said to myself: "Why not giving the guys over there a shot by releasing some stuff and see what they do with it.".

    Beforehand: if you have no clue about how to use IDA and what the fuck I'm talking about: please don't tire me with silly questions.

    I started divin' into CF reverse engineering (with varying activity) about a month ago and the first thing I realized was that, in many cases, it's very easy to find out what the game's code is good for due to the nice "commenting" with debug strings used by the CrossFire developers literally everywhere. There's one function I use to call LogActionf (probably no perfect name as it actually does not log anything but pushes the given format string into CrossFire's debug console), that is used all over the code, probably prototyped somehow like this:
    Code:
    void LogActionf(const char *szFormat, ...);
    Furthermore, there is the following format string used in 543 cases
    Code:
    LT ERROR: %s returned %s (%s)
    where the first placeholder is filled with the calling function's name, the second one with some kind of error "descriptor" (e.g. LT_NOTFOUND) and the third one with a string describing the error. To this format string, in turn, a pointer is addressed which I labeled pLtErrorSReturn. Using this simple logic the script later in this post is able to rename a whole bunch of functions in the game to something better fitting than sub_XXXXXX.

    tl;dr:
    1. Locate format string (LT ERROR: %s returned %s (%s))
    2. Check X-Refs for a data reference (pointer to format-string)
    3. Take a look at the X-Refs to the pointer and see what function it is pushed to (will be LogActionf)
    4. Rename the format-string ptr to pLtErrorSReturn and the function it is pushed to to LogActionf
    5. Fire script and admire how it gives ~300 functions correct names


    Code:
    #include <idc.idc>
    
    static main()
    {
       auto  strAddr     = LocByName("pLtErrorSReturn"), 
             logFunAddr  = LocByName("LogActionf"), 
             curXref, 
             curAddr,
             curTraceAddr,
             firstParamFound,
             funcNameStrAddr,
             regId,
             funAddr,
             funName,
             oldFunName;
          
       for(curXref = DfirstB(strAddr)
          ; curXref != BADADDR
          ; curXref = DnextB(strAddr, curXref))
       {
          // locate function name
          // ------------------------------------------------
       
          // Check mnemonic
          if (GetMnem(curXref) != "mov" 
             || GetOpType(curXref, 0) != o_reg
             || GetOpType(curXref, 1) != o_mem)
          {
             Message("[0x%08X] referencing instruction is not \"mov eXx, [mem]\"!\n", curXref);
             continue;
          }
          
          // Find LogActionf call (max 50 bytes away)
          for (curAddr = curXref
             ; curAddr != BADADDR
             ; curAddr = NextAddr(curAddr))
          {
             // Is call to log fun? Break loop
             if (GetMnem(curAddr) == "call" 
                && GetOpnd(curAddr, 0) == "LogActionf")
             {
                break;
             }
             
             if (curAddr - curXref > 50)
             {
                Message("[0x%08X] too many steps till LogActionf call\n", curXref);
                return;
             }
          }
          
          // Find LogActionf's second parameter
          for (firstParamFound = 0; curAddr != BADADDR; curAddr = PrevAddr(curAddr))
          {
             // Conditional?
             if (RfirstB0(curAddr) != BADADDR)
             {
                Message("[0x%08X] conditional situation\n", curAddr);
                break;
             }
             
             // Desired parameter?
             if (GetMnem(curAddr) == "push")
             {
                if (!firstParamFound)
                   firstParamFound = 1;
                else
                {
                   funcNameStrAddr = 0;
                
                   // In case the operand is a register, we have to trace what
                   // is written into it
                   if (GetOpType(curAddr, 0) == o_reg)
                   {
                      regId = GetOperandValue(curAddr, 0);
                                        
                      for (curTraceAddr = curAddr
                         ; curTraceAddr != BADADDR
                         ; curTraceAddr = PrevAddr(curTraceAddr))
                      {
                         // Does instruction write into our register?
                         if (GetOpType(curTraceAddr, 0) == o_reg
                            && GetOperandValue(curTraceAddr, 0) == regId
                            && GetOpType(curTraceAddr, 1) == o_mem)
                         {
                            funcNameStrAddr = Dword(GetOperandValue(curTraceAddr, 1));
                            break;
                         }
                         
                         // Max. trace distance = 50 non-conditional bytes
                         if (curAddr - curTraceAddr > 50 || RfirstB0(curTraceAddr) != BADADDR)
                         {
                            Message("[0x%08X] conditional situation or trace width exceeded\n", 
                                    curTraceAddr);
                            break;
                         }
                      }
                   } 
                   else if (GetOpType(curAddr, 0) == o_imm)
                      funcNameStrAddr = GetOperandValue(curAddr, 0);
                   else
                   {
                      Message("[0x%08X] unsupported operand\n", curAddr);
                      return;
                   }
                   
                   // check if string contains spaces (filter out names with comments)
                   funName = 0;
                   if (funcNameStrAddr)
                   {
                      if (strstr(GetString(funcNameStrAddr, -1, ASCSTR_C), " ") != -1)
                      {
                         Message("[0x%08X] no valid function name\n", curAddr);
                         funcNameStrAddr = 0;
                      }
                      else
                         funName = GetString(funcNameStrAddr, -1, ASCSTR_C);
                   }
                      
                   break;
                }
             }
          }
          
          // rename function
          // ------------------------------------------------
          
          // only if function's name could be determined
          if (funName)
          {   
             oldFunName = GetFunctionName(curXref);
             
             if (oldFunName != "" && !hasName(GetFlags(LocByName(oldFunName))))
             {
                // make name unique
                while (LocByName(funName) != BADADDR)
                   funName = funName + "_";
             
                Message("[0x%08X] %s => %s\n", LocByName(oldFunName), oldFunName, funName);
                MakeNameEx(LocByName(oldFunName), funName, SN_CHECK | SN_NON_AUTO);
             }
          }
          
          // ------------------------------------------------
          
       }
       
       Message("Done, leaving.\n");
    } // ==> main
    Hope you have some use for that!

    Greets,
    Ende! - **** team
    why not you post all these on your site ? as i see your admin there ...
    lol

  8. #6
    Ende!'s Avatar
    Join Date
    Feb 2010
    Gender
    male
    Posts
    33
    Reputation
    10
    Thanks
    33
    Quote Originally Posted by BlackLite View Post
    why not you post all these on your site ? as i see your admin there ...
    lol
    Why would I do that? There's nobody over there who could have any use for it. 'My' site is not intended to step into competition with sites like MPGH, it's more like a support and release board for our hacks.

  9. #7
    BlackLite's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Posts
    547
    Reputation
    58
    Thanks
    1,035
    My Mood
    Aggressive
    Quote Originally Posted by Ende! View Post
    Why would I do that? There's nobody over there who could have any use for it. 'My' site is not intended to step into competition with sites like MPGH, it's more like a support and release board for our hacks.
    my name there helm i buy farmbot there :P for my 2 computers i pay 25euro psc + 10usd on paypal

  10. #8
    Code[VB]'s Avatar
    Join Date
    Mar 2011
    Gender
    male
    Location
    CODER
    Posts
    608
    Reputation
    11
    Thanks
    702
    My Mood
    Bitchy
    @Ende!
    1. Good work! It's interesting
    2. Why are you with ****?
    - they bought the VIP source from @Zacherl (yeah i know that.. by contacts)
    . - now they are not able to Update becouse cf patched the d3d9.dll entry (Midfunc Hook)
    . - at last, the source of them is written in Delphi (omg)
    3. I will stay on ollydbg but if you think IDA is better, its okay, i never tryed it becouse i can't find an cracked version of it.
    @BlackLite
    1. QQ more, if you don't understand it.. "psssst, i have an tipp for you > leave coding section > go to tutorial section > open up a thread > teach yourselfe coding & understanding posts about developing and such"
    2. leechers like you mages me like *fuck yea, i make bitches wet*

  11. #9
    BlackLite's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Posts
    547
    Reputation
    58
    Thanks
    1,035
    My Mood
    Aggressive
    Quote Originally Posted by Code[VB] View Post
    @Ende!
    1. Good work! It's interesting
    2. Why are you with ****?
    - they bought the VIP source from @Zacherl (yeah i know that.. by contacts)
    . - now they are not able to Update becouse cf patched the d3d9.dll entry (Midfunc Hook)
    . - at last, the source of them is written in Delphi (omg)
    3. I will stay on ollydbg but if you think IDA is better, its okay, i never tryed it becouse i can't find an cracked version of it.
    @BlackLite
    1. QQ more, if you don't understand it.. "psssst, i have an tipp for you > leave coding section > go to tutorial section > open up a thread > teach yourselfe coding & understanding posts about developing and such"
    2. leechers like you mages me like *fuck yea, i make bitches wet*
    lol ? nice useless thread ... i didnt type anything like coding ? and as i told u GTFO TO UR CRAP GK NOOOOOB.
    MPGH DONT NEED NOOBS LIKE U.

  12. #10
    |Skrillex|'s Avatar
    Join Date
    Nov 2011
    Gender
    male
    Posts
    118
    Reputation
    22
    Thanks
    155
    My Mood
    Bored
    @Ende! nice its very usefull..Weiter so
    @BlackLite u are only a fucking leecher!U cant code anything ..U ask for source thats a fail xD go learn c++ dumbass xD
    "In GK are only pro coders but mpgh full with leechers (but not all) u are only one of the leechers."
    Last edited by |Skrillex|; 07-07-2012 at 03:40 AM.

  13. The Following 2 Users Say Thank You to |Skrillex| For This Useful Post:

    Code[VB] (07-07-2012),[mi5 (07-07-2012)

  14. #11
    Code[VB]'s Avatar
    Join Date
    Mar 2011
    Gender
    male
    Location
    CODER
    Posts
    608
    Reputation
    11
    Thanks
    702
    My Mood
    Bitchy
    Quote Originally Posted by BlackLite View Post
    lol ? nice useless thread ... i didnt type anything like coding ? and as i told u GTFO TO UR CRAP GK NOOOOOB.
    MPGH DONT NEED NOOBS LIKE U.
    baahh words from a hobby-less little kid
    you should first think about what your going to write here

    come on.. just a little prove by my side

    my team vips are coming out soon:
    - cf na
    - cf ru
    - cf ph
    - cf eu
    - cf fg

    screen is from cf fg becouse that was the one i updated yesterday
    (menu is just a simple basic from my side becouse we only need to testup the features etc, the real vip is looking better)

    (there is no advertise on it so, no bann reason for me > just look at that site @BlackLite named and you will see identical pictures & threads)

    my job here:
    - helped G-Force (when i was noob)
    - posted my hacks and worked with my bros @derh.acker & @Skrillex
    - i know now more about coding than about 3 years ago
    - yeah i´m still learning that my skill grows (C++ & ASM books)
    - i never will post here on mpgh again becouse the rules are to bad for my releases.. i can´t use my loader (more than 1000 lines perfect code) and and and.. and i hate leechers like you @BlackLite becouse you can see tons of it here on mpgh


    also my last word here.. if i meet you at gk i will f*** y** in your a** and give you instant bann



    Last edited by Code[VB]; 07-07-2012 at 03:48 AM.

  15. The Following 2 Users Say Thank You to Code[VB] For This Useful Post:

    [mi5 (07-07-2012),|Skrillex| (07-07-2012)

  16. #12
    [mi5's Avatar
    Join Date
    Mar 2012
    Gender
    male
    Posts
    301
    Reputation
    10
    Thanks
    618
    man


    Code[VB] plz
    (Download (Download (Download (Download

  17. #13
    kmanev073's Avatar
    Join Date
    Feb 2011
    Gender
    male
    Location
    Bulgaria
    Posts
    2,400
    Reputation
    97
    Thanks
    2,537
    My Mood
    Cool
    Quote Originally Posted by Code[VB] View Post
    @Ende!
    1. Good work! It's interesting
    2. Why are you with ****?
    - they bought the VIP source from @Zacherl (yeah i know that.. by contacts)
    . - now they are not able to Update becouse cf patched the d3d9.dll entry (Midfunc Hook)
    . - at last, the source of them is written in Delphi (omg)
    3. I will stay on ollydbg but if you think IDA is better, its okay, i never tryed it becouse i can't find an cracked version of it.
    @BlackLite
    1. QQ more, if you don't understand it.. "psssst, i have an tipp for you > leave coding section > go to tutorial section > open up a thread > teach yourselfe coding & understanding posts about developing and such"
    2. leechers like you mages me like *fuck yea, i make bitches wet*
    OFFTOPIC: lol they really use delphi to make d3d hacks ?

  18. #14
    BlackLite's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Posts
    547
    Reputation
    58
    Thanks
    1,035
    My Mood
    Aggressive
    Quote Originally Posted by Code[VB] View Post
    baahh words from a hobby-less little kid
    you should first think about what your going to write here

    come on.. just a little prove by my side

    my team vips are coming out soon:
    - cf na
    - cf ru
    - cf ph
    - cf eu
    - cf fg

    screen is from cf fg becouse that was the one i updated yesterday
    (menu is just a simple basic from my side becouse we only need to testup the features etc, the real vip is looking better)

    (there is no advertise on it so, no bann reason for me > just look at that site @BlackLite named and you will see identical pictures & threads)

    my job here:
    - helped G-Force (when i was noob)
    - posted my hacks and worked with my bros @derh.acker & @Skrillex
    - i know now more about coding than about 3 years ago
    - yeah i´m still learning that my skill grows (C++ & ASM books)
    - i never will post here on mpgh again becouse the rules are to bad for my releases.. i can´t use my loader (more than 1000 lines perfect code) and and and.. and i hate leechers like you @BlackLite becouse you can see tons of it here on mpgh


    also my last word here.. if i meet you at gk i will f*** y** in your a** and give you instant bann



    idc about all your shit or you fucking ugly site and i dont even visit it ... LOL hum u say me leecher ? lalz 2 kids gtfo from mpgh go at your site noobs and i dont give a fuck about gforce

    ---------- Post added at 07:07 AM ---------- Previous post was at 07:06 AM ----------

    Quote Originally Posted by |Skrillex| View Post
    @Ende! nice its very usefull..Weiter so
    @BlackLite u are only a fucking leecher!U cant code anything ..U ask for source thats a fail xD go learn c++ dumbass xD
    "In GK are only pro coders but mpgh full with leechers (but not all) u are only one of the leechers."
    who give a fuck or mention a gay like u ? LOLOLOL Seems ur friend dont know to answer me

  19. #15
    Zacherl's Avatar
    Join Date
    May 2009
    Gender
    male
    Posts
    150
    Reputation
    10
    Thanks
    42
    My Mood
    Aggressive
    Quote Originally Posted by Code[VB] View Post
    2. Why are you with ****?
    - they bought the VIP source from @Zacherl (yeah i know that.. by contacts)
    . - now they are not able to Update becouse cf patched the d3d9.dll entry (Midfunc Hook)
    . - at last, the source of them is written in Delphi (omg)
    They only used my old text based menu, because they need a quick release. They are actually very skilled. As you can see in the newer versions of the hack, Saedelaere coded a complete new mouse menu.

    Quote Originally Posted by kmanev073 View Post
    OFFTOPIC: lol they really use delphi to make d3d hacks ?
    BTW: Delphi is a very nice language. It really doesnt matter whether you use delphi or C++ for hacks. It only depends on what you like more, the delphi or the C++ syntax.
    Last edited by Zacherl; 07-07-2012 at 06:23 AM.

  20. The Following 3 Users Say Thank You to Zacherl For This Useful Post:

    Code[VB] (07-07-2012),Ende! (07-07-2012),|Skrillex| (07-07-2012)

Page 1 of 2 12 LastLast

Similar Threads

  1. PC: Script Compive Error, Unknown Function CANT FIX
    By Tuhoaja in forum Call of Duty Modern Warfare 2 GSC Modding Help/Discussion
    Replies: 7
    Last Post: 12-09-2010, 07:28 AM
  2. [Detected] Steam Name Animator v1.1 - Automatic Address!!!
    By hooch in forum Call of Duty 6 - Modern Warfare 2 (MW2) Hacks
    Replies: 37
    Last Post: 10-12-2010, 02:22 PM
  3. Script compile error - Unknown function (check console for details)
    By Chaojon in forum Call of Duty Modern Warfare 2 Help
    Replies: 40
    Last Post: 07-01-2010, 09:20 PM
  4. how to make script for KOS to change name ingame??
    By nicolas0008 in forum C++/C Programming
    Replies: 3
    Last Post: 04-02-2010, 09:07 AM
  5. HShield automatic file mover - Batch Script
    By Divine Will in forum Combat Arms Hacks & Cheats
    Replies: 31
    Last Post: 12-23-2008, 06:44 PM