# Be kind to people, dont condemn people, but if they need or want criticism give it to them kindly, and if they dont need or want criticism then TALKDa best you can about them! # ∀m ∈ People (bekindto(you, m) ∧ ¬condemn(you, m) ∧ (needsorwantscriticism(m) -> givecriticismkindlyto(you, m)) ∧ (¬needsorwantscriticism(m) -> talkdabestabout(you, m))) # I wonder what this is for? =) VVVVVVV * # Rd := Destination register. * # Rs := Source register. * # Rsd := Register that is both read from and written to. * # roi := Register or immediate. * # imm := Immediate. * # label := PC relative or literal address to jump to. * # cond := condition. * # smf := Source modifier, may be optional or not. * # m := The size of the destination register in bits or if the instruction has no destination register then the size of the first source register in bits, either 32 or 64. * # <...> := Optionally pick whatever is in <>. * # (...) := Required, pick whatever is in (). * # A|B := Pick either A or B. * # Optional or required stuff may contain stuff like (A|B) or <A|B\>, for the required section you must pick either A or B, for the optional section you must select A, B, or nothing. * # <Z|SP\>X := A 64 bit general purpose register including XZR if X starts with Z or SP if X starts with SP. * # <Z|SP\>W := The lower 32 bits of a 64 bit general purpose register including WZR if W starts with Z or WSP if W starts with SP. * # <Z|SP\>G := X or W, including XZR or WZR if G starts with Z, including SP or WSP if G starts with SP. * # There can be multiple destination and source registers, written like: Rd1, Rd2, Rd3, etc for destination register and like this for source registers: Rs1, Rs2, Rs3, etc. There can also be multiple Rsds, written like: Rsd1, Rsd2, Rsd3, etc. There can be multiple immediate numbers, written like: imm1, imm2, imm3, etc. There can be multiple rois written like: roi1, roi2, roi3, etc. ## Part 1: Reverse engineering First of all, I copied talkd to ~/hope (hope I can find a vuln here lmao), then I opened it in ida pro! ![[ida_talkd_01.jpg]] gethostname is called with a length of 255 and if the result's 31st bit is not zero then we exit. The current working directory is attempted to be set to /dev/, if the attempt fails then we exit. If there was no command line argument then we jump to loc\_1000018A8, if not then we check if the first command line argument does not start with - then we go jump to loc\_1000018A8. ![[ida_talkd_02.png]] If the next character of the first command line argument does not start with d then PC is set to loc\_1000018A8, if the next character is not zero then we branch to loc\_1000018A8. 1 is stored at \[X9 + dword\_100008498\]. At loc\_1000018A8 the pseudo instruction ADRL is used to load 0x1000019C0 into X16 and then signal is called with the first argument being 14 and the next argument being that address in X16, being treated as a function. The function is just this: ![[ida_talkd_03.png]] Note that 0x10008478 is in bss. Anyways, after signal is done, alarm is called with 0x1E in W0, note that W0 is the lower 32 bits of X0 and writing to a W register zero extends the entire X version of that same register, X21 is set to the result of the bitwise ORR operation between SP + 0x60 + var\_50 and 2. X22 is set to the page where qword\_100008478 is in. W23 is set to 16, X20 is set to 0x100008480, X24 is set to a pointer to the string "sendto: %m" and X25 is set to a pointer "recv: %m". ![[ida_talkd_04.png]] Next, at loc\_1000018F8 W0 is set to 0, X1 is set to X19, X2 is set to 0x54, remember setting a W register zero extends the entire X register. W3 is set to 0 and recv is called and if the bytes received was not 0x54 then the 31st bit of W0 is checked to see if it is zero and if it is then we jump to loc\_1000018F8, if it isnt \_\_error is called and W8 is set to 32 bits at X0 being treated as an address and X2 is set to X25, if W8 is 4 then we jump to loc\_1000018F8, else call a logging function with W0 being set to 4 and then we jump to loc\_1000018F8. Now, if 0x54 bytes were received time is called with X0 being set to 0 and the result is stored in 0x100008478. X8 is set to 64 bits at X19 plus 0x1A being treated as an address and is stored at X21 being treated as an address. X8 is set to 64 bits at X19 plus 0x20 being treated as an address and is stored at X21 plus 6 being treated as an address. W8 is set to the 8 bits at X19 plus 0x19 being treated as an address and the lower 8 bits of W8 is stored at SP + 0x60 + var\_50.sa\_family being treated as an address. Now, 8 bits at SP + 0x60 + var\_50.sa being treated as an address is set to the lower 8 bits of W23. and sub\_1000009E4 is called with X19 as the first argument and X20 as the second argument. X4 is set to SP plus 0x60 + var\_50. W0 is set to 0, X1 is set to X20, W2 is set to 0x18, W3 is set to 0, and W5 is set to 16, and then sendto is called. X1 is set to X24 and if 0x18 bytes were sent then we jump to loc\_1000018F8 else a logging function is called with W0 being set to 4 and then we jump to loc\_1000018F8. Now, in sub\_1000009E4 this happens: ![[ida_talkd_05.png]] First of all, X19 is set to the second argument, then the zeroth byte of the second argument is set to 1, the first byte of the second argument is set to the first byte of the first argument, 4 bytes starting at the 4th byte of the second argument are set to 0. If the zeroth byte of the first argument is not 1 we jump to loc\_100000B14, X20 is set to the first argument. 4 bytes starting at byte 4 at the first argument is copied to W8 and the byte order of W8 is reversed and 4 bytes starting at byte 4 at the first argument is set to W8. The byte order of 2 bytes starting at byte 8 at the first argument is reversed. The value of those 2 bytes together before they swapped places is checked to be 0x200, if the value is not 0x200 then we jump to loc\_100000B30. The byte order of 2 bytes starting at byte 0x18 at the first argument is reversed. The value of those 2 bytes together before they swapped places is checked to be 0x200, if the value is not 0x200 then we jump to loc\_100000B50. Now, we are here: ![[ida_talkd_06.png]] If byte 0x2C of the first argument is 0 then we jump to loc\_100000A9C, W0 is set to the lower 8 bits of W8 and the bits above bit 7 of W0 are set to bit 7 of W8, then bit 7 of W8 is checked if it is not zero, and if it is not zero we go to loc\_100000A88 where there is a check if W0 is printable, if W0 is not printable then we go to loc\_100000B90, if W0 is printable we go to loc\_100000A94. If bit 7 of W8 is 0 then there is a check to see if W0 is printable, if it is we go to loc\_100000A94 where W8 is set to 4 bytes at X21 being treated as an address and X21 is increased by 1 and if W8 is not 0 then we go to loc\_100000A6C. If it is 0 then we go to loc\_100000A9C. At loc\_100000A9C W8 is set to 4 bytes at the first argument plus 0x28 being treated as an address and the 4 bytes in W8 are reversed and 4 bytes at the first argument plus 0x28 being treated as an address are set to W8. X21 is set to 0x100008498 and W8 is set to 4 bytes at X21 being treated as an address and if W8 is 0 then we branch to loc\_100000AC8. If not, sub\_100001310 is called with a pointer to the string "process\_request" and the first argument, and then go to loc\_100000AC8. At loc\_100000AC8 8 bits at the first argument plus 1 being treated as an address are copied to W8 and W8 is checked if it is bigger than 1 and if it is we go to loc\_100000B70, before looking at what is next, lets check out sub\_100001310. ![[ida_talkd_07.jpg]] Wow! It is a logging function, how nice! Anyways, we left off here, so let us keep going! ![[ida_talkd_08.png]] Anyways, remember if W8 is greater than 1 then we go to loc\_100000B70, at loc\_100000B70 if W8 is equal to 2 then we go to loc\_100000BE4 which calls sub\_10000126C with 8 bits at X20 plus 4 being treated as an address and the result is written to the byte at X19 plus 2 and then PC is set to loc\_100000C0C. So if W8 was not equal to 2 then we check if it is not equal to 3 and if it isnt equal to 3 then we go to loc\_100000BBC and there W8 is set to 5 and PC is set to loc\_100000BF8 and there W8 is set to 8 bits at X19 plus 2 being treated as an address and then PC is set to loc\_100000C0C. If W8 is equal to 3 then sub\_100000C44 is called with the first and second argument and after that, PC is set to loc\_100000C0C. If W8 is not greater than 1 then there is a check if W8 is zero or not, if W8 is zero then sub\_100001014 is called with the first argument, if the function returned 0 then PC is set to loc\_100000C00 and at loc\_100000C00 sub\_10000113C is called with the first and second argument then PC goes to loc\_100000C0C. If that function did not return 0 then W8 is set to 32 bits at the result of the function plus 4 being treated as an address and W8's bytes are reversed and W8 is stored at X19 plus 4 being treated as an address. 8 zero bits are stored at X19 plus 2 being treated as an address and PC is set to loc\_100000C0C. If W8 is not 1 then PC is set to loc\_100000BBC, if W8 is 1 then sub\_100000E80 is called with the first argument and if the result of the function is 0 then W8 is set to 1 and PC is set to loc\_100000BF8. If the result of that function is not 0 then 4 bytes at the result of the function plus 4 is stored at the second argument plus 4 being treated as an address. 16 bytes at the result of the function plus 8 are stored at the second argument plus 8 being treated as an address. W8 is set to 2 bytes at the result of the function plus 8 being treated as an address, the bytes of W8 are reversed and W8 is shifted to the right by 16 and is stored at the second argument plus 8 being treated as an address. PC is set to loc\_100000BDC. Now we are here: ![[ida_talkd_09.png]] If 4 bytes at X21 being treated as an address are zero then we return, else sub\_1000013D4 is attempted to be called with a pointer to the string process\_request and the second argument. Now that we covered sub\_1000009E4, let's cover these functions: sub\_10000126C, sub\_100000C44, sub\_100001014, sub\_10000113C, sub\_100000E80, and sub\_1000013D4, starting with sub\_10000126C. ![[ida_talkd_10.png]] X19 is set to X0, the first argument of this function and X22 is set to 0x100008498 and 4 bytes at X22 being treated as an address are copied to W8, if W8 is zero we skip the logging function, else the logging function is called. X8 is set to the page where 0x100008410 is and X20 is set to 8 bytes at 0x100008410 and if X20 is 0 we return 1, if X20 is not 0 then X21 is set to 0x100001E91 and PC is set to loc\_1000012C0. W8 is set to 4 bytes at X20 plus 4 being treated as an address, if W8 is equal to W19 then we go to loc\_1000012F0, at that location X0 is set to X20 and sub\_100000F88 is called and after it is called W0 is set to 0 and the function returns, else W8 is set to 4 bytes at X22 being treated as an address. If W8 is 0 then PC is set to loc\_1000012E0. The function sub\_100001310 is called with X21 and X20, PC is set to loc\_1000012E0 and at loc\_1000012E0 X20 is set to 8 bytes at X20 plus 0x60 and X20 is checked if it is not zero and if it is not zero then PC is set to loc\_1000012C0. If X20 is zero then this function returns 1 else we go to loc\_1000012F0. Now, lets look at sub\_100000F88. ![[ida_talkd_11.png]] The only argument for this function is stored in X19, W8 is set to 4 bytes at 0x100008498, if W8 is zero then we skip that logging function else it gets called. X9 is set the whatever page 0x100008410 is in and X8 is set to 8 bytes at 0x100008410. If X8 is equal to X19 then we go to loc\_100001004 where X8 is set to 8 bytes at X19 plus 0x60 being treated as an address, X8 is stored at 0x100008410 and if X8 is zero we go to loc\_100000FE4 else we go to loc\_100000FDC. If X8 is not equal to X19 then X8 is set to 8 bytes at X19 plus 0x60 being treated as an address and X9 is set to 8 bytes at X19 plus 0x68 being treated as an address, if X9 is zero then jump to loc\_10000FD8 else X8 is copied to X9 plus 0x60 being treated as an address and PC is set to loc\_10000FD8. If X8 is zero then we go to loc\_100000FE4 else we go to loc\_100000FDC and at loc\_100000FDC, X9 is set to X19 plus 0x68 being treated as an address and X9 is copied to X8 plus 0x68 being treated as an address and PC is set to loc\_100000FE4. At loc\_100000FE4... well it's just a cleanup and safety thing, assuming all is well, \_free is called with X19. The next function to look at is sub\_100000C44: ![[ida_talkd_12.png]] The second argument of that function is stored in X19 and the first argument of that function is stored in X20, sub\_100000D00 is called with the first argument plus 0x38 and the first argument plus 0x44, if the result of that function was not zero then we return, else we set X0 to the first argument plus 0x1C, W1 is set to 4, W2 is set to 2 and \_gethostbyaddr is called, if the result is 0 then we set W0 to 3 and return. X21 is set to the result of \_gethostbyaddr and sub\_100001014 is called with the first argument, if the resulst of the function is zero we jump to loc\_100000CC4 where sub\_10000113C is called with the first and second argument and then PC is set to loc\_100000CD0. If the result of sub\_100001014 was not zero then X22 is set to the result of sub\_100001014 and W9 is set to 4 bytes at X20 plus 4 being treated as an address and W8 is set to X0 plus 4 being treated as an address and if W9 is less than W8 then W0 is set to 0, the bytes in W8 are reversed and W8 is stored at X19 plus 4 being treated as an address and the function returns. If W9 is not less than W8 then sub\_10001234 is called and the result is stored in X22 plus 4 being treated as an address and W8 is set to the bytes reversed of the result and W8 is stored in X19 plus 4 being treated as an address and PC goes to loc\_100000CD0. At loc\_100000CD0, sub\_1000014CC is called with the first argument and 8 bytes at X21 being treated as an address and after that the function returns. In this function, let's look at sub\_100000D00 first! ![[ida_talkd_13.png]] X19 is set to the second argument and X20 is set to the first argument, after some setup \_setutxent is called... What on God's green earth are \_setutxent, \_getutxent, \_endutxent (look at the image below for that last one lol)???? Time to research! After millions of hours of research (pulling up the man page!) I figured out what these functions are and what they do! So first of all, there is this file called utmpx in this directory /var/run/, and that file contains a certain number of entries! Now, to learn more about this thingy I made a little parser: ``` .global _main .section __TEXT,__text _main: ORR X22, X30, XZR SUB SP, SP, #64 BL _setutxent ORR W19, WZR, WZR _get_entries: BL _getutxent CBZ X0, _quit STP X19, X0, [SP] LDR W1, [X0, #256] REV W1, W1 ADD X2, X0, #260 STP X1, X2, [SP, #16] ADD X1, X0, #292 LDP W1, W2, [X1] STP X1, X2, [SP, #32] ADD X0, X0, #304 ADD X20, X0, #16 LDR X0, [X0] STR X0, [SP, #48] ADD X0, SP, #48 BL _ctime STP X0, X20, [SP, #48] ADRP X0, entry@PAGE ADD X0, X0, entry@PAGEOFF ADD X19, X19, #1 BL _printf B _get_entries _quit: BL _endutxent ADD SP, SP, #64 ORR X30, X22, XZR MOVZ W0, #225 RET .section __DATA,__data entry: .asciz "ENTRY %lu\n\tLOGIN NAME: %.256s\n\tID: 0x%08x\n\tTTY NAME: %.32s\n\tPROCESS ID: %d\n\tTYPE: %hd\n\tTIME: %.25s\tHOST NAME: %.256s\n" ``` I got 17 entries in total (0-16) and ENTRY NUMBER SEVENTEEN SEEMS VERY VERY INTERESTING... (☟︎⚐︎🏱︎☜︎ ✡︎⚐︎🕆︎ ☝︎⚐︎❄︎ ❄︎☟︎☜︎ ☼︎☜︎☞︎☜︎☼︎☜︎☠︎👍︎☜︎ ☹︎⚐︎☹︎). Anyways, lets get back to it! So, \_setutxent opens the file I mentioned above and seeks to the start of the file, \_getutxent reads an entry and seeks to the next entry, and \_endutxent closes the file. Also the string /dev/ is stored at X29 plus var\_7c, we then get the first entry and if it is NULL we branch to loc\_100000E30 and that just sets 1 to the return value. If the first entry is not NULL then: ![[ida_talkd_14.png]] After some setup happens like X25 being set to 0, PC is set to loc\_100000D88 and if the TYPE field of the current entry is not 7 then PC is set to loc\_100000DFC. If the LOGIN NAME does not have the same contents of the first argument then we go to loc\_100000DFC. If X25 is not zero then we jump to loc\_100000DB0, If the first byte at the second argument is not zero then we go to loc\_100000DEC. Now, we are at loc\_100000DB0 where if X25 is zero then W21 is set to W24 or else set to W21 (lol), quick break cuz I just wanna say I like the CSEL instruction, I want to know where the idea itself came from... If you want to learn about the CSEL instruction here is my definition of it! * # CSEL Rd1, Rs1, Rs2, cond * ## Rd1, Rs1, and Rs2 can be a GZ. They must share the same type. * ## If cond is true then Rd1 is set to Rs1, if cond is false then Rd1 is set to Rs2. Ok unpause! stat is called on the file in /dev/ with the same name as the TTY NAME field in the entries and if the result is not zero we branch to loc\_100000DEC and there is a check if the second argument is equal to the TTY NAME field of the current entry and if the strings match we return 0 else we jump to loc\_100000DFC and there the next entry is grabbed and is checked if it is NULL and if it is not NULL then we jump to loc\_100000D88 else the function returns. Anyways, if the result of stat was zero then \[SP,#0x110+var\_110+4\] is checked if it's 4th bit is 0 and if it is we go to loc\_100000DFC else 8 bytes at \[SP,#0x110+var\_F0\] are checked if they are greater than X25 and if they are we jump to loc\_100000E0C else we jump to loc\_100000DEC. At loc\_100000E0C X19 is set to hold the current entry's TTY NAME and the next entry is grabbed, W12 is set to 0, X25 is set to X26, and if the current entry is NULL we return, else we jump to loc\_100000D8... Ok that function was pretty cool! I didn't know anything about those functions before! You know like I wonder what a logic machine would even look like and how would it work, id classify it as a computer because of the rules and state but how practi--- ANYWAYS! Now, we have sub\_100001014! time to PTRACE_PEEK this function (lol): ![[ida_talkd_15.png]] \_clock\_gettime is called the first argument being 6 and the next argument qword\_100008400 being the buffer. After that function is done, the first 8 bytes of the buffer are stored in X23, X24 is set to 0x10008494 and if the 4 bytes starting at that address are zero we branch to loc\_10000106C, else we call sub\_100001310 with the string "find\_request" and the argument for sub\_10001014. (the call for sub\_100001310 is not in this above or below image but it is here 1 image below the image below... Sorry, but atleast you can see sub\_10001310!) ![[ida_talkd_18.png]] The first byte of the second argument is compared against 5 and if the carry flag is cleared then some logging stuff happens with "leave_invite" else it happens with "type %d" and %d is replaced with whatever X8 is. The function just logs stuff and returns! After sub\_100001310 returns PC is set to loc\_10000106C, Now at loc\_10000106C: ![[ida_talkd_16.png]] If X25 is set to the 8 bytes at 0x100008410 and if they are NULL then we return 0 else X0 is set to the address of the string "deleting expired entry" and X21 is set to 0x100001E91 and at that address 0 is stored. PC is set to loc\_100000EF4 and there X22 is set to X25 and X25 is set to the value at X25 plus 0x60 being treated as an address, now the value at X22 + 0x58 being treated like an address minus the first 8 bytes of the buffer that was called with \_clock\_gettime in this function is checked if it is less than 0x3d and if it is less than 0x3d then there is a check if the 4 bytes at X22 + 0x58 being treated like an address (ALSO STORED IN X8) is zero and if it is then we jump to loc\_1000010D0, else we call sub\_100001310 is called with 0x100001E91 and X22 and then PC is set to loc\_1000010D0. At loc\_1000010D0 a strcmp takes place with the argument plus 0x38 is compared against X22 plus 0x38, if they dont match PC is set to loc\_100001110 and at the location if X25 is not zero we jump to loc\_100001088 else we return 0. Anyways, another string comparison takes place with the argument plus 0x2C is compared against X22 plus 0x2C, if they dont match PC is set to loc\_100001110, if the first byte of both the argument and X22 dont match then we jump to loc\_100001110 and if 8 bytes at the argument plus 0x28 and 8 bytes at X22 plus 0x28 dont match we go to loc\_100001110 else we go to loc\_100001134 and there X23 is stored at X22 plus 0x58 and the function returns. Now, lets back up to the check with the value at X22 + 0x58 being treated like an address (ALSO STORED IN X8) minus the first 8 bytes of that buffer compared to 0x3D, if that is not less than 0x3D then there is a check if W8 is 0 and if it is then PC is set to loc\_1000010B4 and that calls sub\_100001310 with the address of the string "deleting expired entry" and X22, and after that function returns then PC is set to loc\_1000010B4 and there, sub\_100000F88 is called with X22 and then PC is set to loc\_100001110. That function is covered! Let's keep going! But first, I noticed I did not cover the paths for loc\_100000B14, loc\_100000B30, loc\_100000B50, and loc\_100000B90... So the image below shows them! ![[ida_talkd_17.png]] Now, we have this function, sub\_10000113C: ![[ida_talkd_19.jpg]] \_clock\_gettime is called with 0x100008400 as the buffer and 6. The pseudo instruction SMULL is used to do a signed multiplication on W9 and W10 and the result is stored in X10, and W9 contains 4 bytes at 0x100008418 plus 1, and W10 is equal to 0x10624DD3. SMULL is an alias for the instruction SMADDL with the 4th argument of SMADDL being equal to XZR. Also did you know that the MOV instruction is also an alias for the following instructions: ADD, ORR, MOVN, and MOVZ. Anyways, lets keep going! I spy with my little eye 👁️, the pseudo instruction ASR used with 0x2A as the third argument! ASR is an alias for either ASRV or SBFM, Now here is my definition of BFM! * # <S|U\>BFM Rd1, Rs1, imm1, imm2 * ## Rd1 and Rs1 can be a GZ. They must be the same type. * ## Both imm1 and imm2 must be 0 to m - 1. * ## If imm2 is greater than or equal to imm1 then the range of bits from imm1 through imm2 in Rs1 are copied into Rd1 starting at bit 0. If the instruction does not start with S or U then the bits outside the range stay the same, if the instruction starts with S then the bits outside the range are set to bit imm2 of Rs1, if the instruction starts with U then the bits outside the range are set to 0. * ## If imm1 is greater than imm2 then, imm2 + 1 bits are copied from Rs1 starting at the lowest bit into Rd1 at bit position m - imm1. The bits outside of the range stay the same if the instruction does not start with S or U, if the instruction starts with S then the bits above are set to bit imm2 of Rs1 while the lower bits are set to 0, if the instruction starts with U then the bits outside the range are set to 0. Now, here is my definition of ASRV! * # ASRV Rd1, Rs1, Rs2 * ## Rd1, Rs1, and Rs2 can be a GZ. They must be the same type. * ## Rd1 is set to Rs1 shifted to the right by Rs2 MOD m and the bits from m - 1 to m - Rs2 MOD m of Rd1 are set to copies of Rs1's sign bit. Can you get guess what ASR X10, X10, \#0x2A really is? If not it is all good! ASR X10, X10, \#0x2A becomes SBFM X10, X10, \#0x2A, \#0x3F. Now, 0x3F is just 63 in hexadecimal! Now, lets ACTUALLY get back to it! After the value of X10 is shifted, W10 is set to W10 times the result of W10 shifted to the right by 31 bits and the rest of X10 is zero extended, Now W9 is set to W9 minus W10 times W11. If the zero flag is set to 0 and the carry flag is set to 1 because of the comparison between W9 and 1 then W9 stays the same (gets set to W9 lol) else W9 is set to 1. W9 is stored at 0x100008418 and W9 is also stored at the first argument plus 4, and W9 with the byte order reversed is stored at the second argument plus 4 being treated as an address. \_malloc\_type\_malloc is called with 0x1020040D04D8E3C as the type id and 0x70 as the size and if the result is NULL we log "insert\_table: Out of memory" and the entire program exits! Now, 8 bytes at 0x100008400 are stored at byte 0x58 of the new allocated thing. 32 bytes from the from the first argument starting at byte 0 are stored in the new allocation starting at byte 0. Also 4 bytes at the 0x50th byte of the first argument are stored at the 0x50th byte of that allocation. Now, 32 bytes starting at byte 0x30 of the first argument are stored at byte 0x30 of the allocated space and 16 bytes starting at byte 0x20 of the first argument are stored at byte 0x20 of the allocation. Now, 8 bytes at 0x100008410 are stored at the 0x60th byte of the allocation that i use different words to talk about each time i mention it, also if the value of the 8 bytes at 0x100008410 are not 0 then the address of the allocation created with \_malloc\_type\_malloc is stored at the 0x68th byte of the value of the 8 bytes at 0x100008410 plus 0x68 being treated as an address. Now, 0 is stored at the 8 bytes starting at the memory marked as allocated plus 0x68 being treated as an address. Next, the address of the allocated space is stored at 0x100008410 and the function returns! Now, we have sub\_100000E80: ![[ida_talkd_20.png]] First of all, \_clock\_gettime is called with 6 and 0x100008400 being the buffer, if 4 bytes at 0x100008498 are not zero then then sub\_10001310 is called with the string "find\_match" and the only argument for this function, then PC is set to loc\_100000ED8, if the 4 bytes were 0 then PC is just set to loc\_100000ED8, at loc\_100000ED8, X8 is set to the page 0x100008410 is in and X25 is set to 8 bytes at 0x100008410 being treated as an address, if X25 is 0 we return 0, else X20 is set to the address of the string "deleting expired entry", X21 is set to 0x100001E91, and PC is set to loc\_100000EF4, in loc\_100000EF4, X22 is set to X25 and X25 is set to 8 bytes at X25 plus 0x60 being treated as an address and if the value of 8 bytes at X22 plus 0x58 being treated as an address minus the first 8 bytes at that buffer that \_clock\_gettime was called with is less than 0x3D then if 4 bytes at 0x is 0 then PC is set to loc\_100000F3C else sub\_100001310 is called with X21 and X22 and then PC is set to loc\_100000F3C, and there is a string compare starting at the 0x2cth byte of the argument and the 0x38th byte of X22, if they are not equal we go to loc\_100000F64 and there is another string comparison with the 0x38th byte of the argument and the 0x2cth byte of X22, if they are not equal we go to loc\_100000F64, if the byte at X22 plus 1 as an address is 0 then we return, else PC is set to loc\_100000F64 where X25 is checked if it is not zero and if it is not 0 we jump to loc\_100000EF4, else 0 is returned. Now, if that value is greater than or equal to 0x3D then if 4 bytes at 0x100008498 are 0 then we jump to loc\_100000F20 else sub\_100001310 is called with X20 and X21 and then PC is set to loc\_100000F20 and there sub\_100000F88 is called with X22 and PC is set to loc\_100000F64. Now we have this function! sub\_1000013D4: ![[ida_talkd_21.png]] If the byte at the second argument plus 1 being treated as an address compared against 5 clears the carry flag then X9 is set to 0x100004150 and X21 is set to X9 plus the value of the byte at the second argument plus one being treated as an address and the byte there is zero extended and shifted to the left by 3 bits, all being treated as an address, and PC is set to loc\_100001440. If the carry flag was not cleared then snprintf is called with SP plus 0x100 plus \_\_str, 0x50, the address of the string "type %d", and that byte that was compared against 5 and then PC is set to loc\_100001440. At loc\_100001440 W8 is set to the byte at the second argument plus 2 being treated as an address and W8 is compared against... "compared against" you know what else is "compared against"??? My sanity and the fact that the apple M5 chip, the apple M5 pro chip, and the apple M5 max chip DO NOT HAVE THE SMIN, SMAX, UMIN, UMAX, ABS, CNT, AND CTZ INSTRUCTIONS! LIKE WHY... wait am I wrong??? I was wrong I just needed to add `.arch_extension cssc` to my code, everything is fine! Anyways, as I was saying W8 is compared against 0x2a and if the carry flag was cleared based on that comparison we jump to loc\_10000146C where X9 is set to the address of the string "success" at 0x100004170 and X22 is set to the value at X9 plus W8 zero extended and shifted to the left by 3 bits all being treated as an address and then PC goes to loc\_100001478. If the carry flag was not set then snprinf is called with SP + 0x100 + var\_D8 as the buffer, 0x50 as the size, answer %d, and the value of X8, then PC is set to loc\_100001478. A logging function is called with 7, %s: %s: %s, id %d, X22, X8, X19, and X21. and we return... Now, I noticed I forgot the document the functions sub\_100001234 and sub\_1000014CC... Uh so let's document them starting with sub\_100001234! ![[ida_talkd_22.png]] If 4 bytes at 0x10624DD3 minus the product of 4 bytes at 0x10624DD3 and 0x100008418 plus 1, shifted to the right by 0x2A (look at SBFM), and then added by itself shifted to the right by 31 bits times 0x3E80 compared against one sets the carry flag and clears the zero flag then we return that value else we return 0. Now, we have sub\_1000014CC: ![[ida_talkd_23.png]] 32 zero bytes are stored at SP + 0xF0 + var\_E0.st\_blksize, SP + 0xF0 + var\_E0.st\_birthtimespec, SP + 0xF0 + var\_E0.st\_mtimespec, SP + 0xF0 + var\_E0.st\_uid, and at SP + 0xF0 + var\_E0. Now, snprintf is called with 0x20, X29 + \_\_str as the buffer, %s%s, the address of the string "/dev/", and the first argument plus 0x44. stat is called with X29 + \_\_str and SP + 0xF0 + var\_E0 and if the 0x1F bit of the result is not zero we return 4 else SP + 0xF0 + var\_E0.st\_mode is checked if the 4th bit is 0 and if it is we return 4, else we go to loc\_100001558 and there sub\_100001598 is called with the first argument plus 0x44, the first argument, and the second argument and after that function is called sub\_1000014CC returns, Now what will sub\_100001598 return is what i want to know! Let us look at sub\_100001598: ![[ida_talkd_24.png]] First of all, 16 0 bytes are stored at SP + 0xAD0 + var\_A88 and at SP + 0xAD0 + var\_AA0 and then \_gettimeofday is called with SP + 0xAD0 + var\_A88 and 0. Now, 8 bytes at SP + 0xAD0 + var\_A88 being treated as an address are stored at SP + 0xAD0 + var\_A90 and SP + 0xAD0 + var\_A90 is called \_localtime and 0x20 being treated as a halfworld is stored at SP + 0xAD0 + \_\_s, and \_strlen is called with SP + 0xAD0+\_\_s. The result of the \_strlen as 4 bytes is stored at W23 and that is copied into SP + 0xAD + var\_574 being treated as an address and then 4 bytes at the result of \_localtime + 16 being treated as an address plus 1 zero extended are stored at SP + 0xAD0 + var\_AB0, 4 bytes at \_localtime + 12 being treated as an address, zero extended are stored at SP + 0xAD0 + var\_AB8. Now, 4 bytes at the result of \_localtime + 4 being treated as an address, zero extended are stored at SP + 0xAD0 + var\_AC0 and 4 bytes at the result of \_localtime + 0x18 being treated as an address, plus 0x76C zero extended are stored at SP + 0xAD0 + var\_AC8. 8 bytes equal to 0x10000849C are stored at SP + 0xAD0 + var\_AD0, 4 bytes at the result of \_localtime + 8 being treated as an address, zero extended are stored at SP + 0xAD0 + var\_AD8. Now all of that stuff is used in this snprintf with the size being 0x100, the buffer being SP + 0xAD0+\_\_s + 0x100, and the format string "Message from Talk\_Daemon@%s at %d:%02d on %d/%.2d/%.2d ...". The buffer's length as 4 bytes is stored in SP + 0xAD0 + var\_570. If W23 is bigger than the result of the string length then W23 stays the same else it is set to the string's length. Now, strlen is called with the second argument plus 0x2C and the result of the bitwise OR operation on 1 and the second argument + 0x2C's string length shifted to the left by 2 bits and 0xC6A0BD8A are used in a call to \_malloc\_type\_malloc and the result is stored in X22 and the \_strvis function is called with the result of the allocation, the second argument plus 0x2C, and 2. So, \_snprintf is called with SP + 0xAD0+\_\_s + 0x200, 0x100, the address of the string "talk: connection requested by %s@%s", X22, and the third argument. After that call is done, the string length of SP + 0xAD0+\_\_s + 0x200 in 4 bytes is stored at SP + 0xAD0 + var\_56C and If W23 is bigger than the result of the string length then W21 is set to W23 else it is set to the string's length. Now, snprintf is called with SP + 0xAD0+\_\_s + 0x300, 0x100, the address of the string "talk: respond with: talk %s@%s", X22, and the third argument. SP + 0xAD0+\_\_s + 0x300's string length as 4 bytes is stored at SP + 0xAD0 + var\_568 and W21 is compared against that string length, if W21 is bigger than the length then W20 is set to W21 else W20 is set to the string length. 0x20 as a halfwold is stored at SP + 0xAD0 + var\_160. The string length of SP + 0xAD0+\_\_s + 0x400 as 4 bytes is stored in SP + 0xAD0 + var\_564, X22 is set to 0 for a loop that is about to happen. If W20 is bigger than W0 then W8 is set to W20 else W8 is set to W0, and W8 is set to the bitwise AND operation between W8 and the bitwise NOT of W8 arithmetically shifted to the right by 31 bits and 0xD07 is stored as a halfword at SP + 0xAD0 + var\_A74. Now, X20 is set to SP + 0xAD0 + var\_A74 + 3, and 0xa as a byte is stored at SP + 0xAD0 + var\_A72. W23 is set to W8 plus 2 and W25 is set to W8 plus 1, X26 is set to the result of the bitwise or of SP + 0xAD0 + \_\_s + 1 and 1, X27 is set to SP + 0xAD0 + var\_574, W28 is set to 0xA0D, and PC is now at loc\_10000173C. At loc\_10000173C, X8 is set to the value at X24 plus X22 shifted to the left by 8 as an address, and if it is 0 PC is set to loc\_100001758, else X9 is set to X26 and PC is set to loc\_10000174C and there W8 at a byte is stored at X20 and X20 is increased by 1 and W8 is set to a byte at X9 and X9 is increased by 1, and if W8 is not 0 then PC is set to loc\_10000174C, else PC is set to loc\_100001758. At loc\_100001758, W8 is set to X27 + X22 shifted to the left by 2, being treated as an address, and if W8 is greater than W23, PC is set to loc\_100001780, else W8 is set to W25 minus W8, X21 is set to X8 plus 1, memset is called with X20, 0x20, and X21, X20 is increased by X21, and then PC is at loc\_100001780. Now, at loc\_10000178 W28 is stored at X20 and X20 is then increased by 2, X22 is increased by 1 and X26 is increased by 0x100, and if X22 does not equal 5 then PC is set to loc\_10000173C. After the loop, 8 zero bytes are stored at X20, X8 is set to SP + 0xAD0 + var\_A74 and X9 is set to X20 minus X8, X8 is stored at SP + 0xAD0 + var\_AA0 and X9 is stored at SP + 0xAD0 + var\_AA8. Now, sub\_100000628 is called with SP + 0xAD0 + var\_AA0, 1, X19, and 0x19. If the result of that function is not equal to 0 then 2 is returned else 0 returns! Now, we are here at sub\_100000628, the final function to look at in this entire binary! ![[ida_talkd_25.jpg]] Now, X21 is set to SP + 0x370 + var\_358 and 16 zero bytes are stored at X21 + 0xEF, X21 + 0xE5, X21 + 0xD5, X21 + 0xC5, X21 + 0xB5, X21 + 0xA5, X21 + 0x95, X21 + 0x85, SP + 0x370 + var\_2E3, SP + 0x370 + var\_2F3, SP + 0x370 + var\_303, SP + 0x370 + var\_313, SP + 0x370 + var\_323, SP + 0x370 + var\_333, SP + 0x370 + var\_343, and SP + 0x370 + var\_353. Now, 8 bytes from qword\_100001CD0 truncated to 4 bytes are stored at SP + 0x370 + var\_358 being treated as an address, 0x2F is stored at SP + 0x370 + var\_354 as a byte. If the second argument is less than 0x20, then PC is set to loc\_1000006CC, else X19 is set to the address of the string "too many iov's (change code in wall/ttymsg.c)" and the function returns with X19 as the return value: ![[ida_talkd_26.jpg]] Now, at loc\_1000006CC we have this: ![[ida_talkd_27.png]] Now, the fourth argument is copied to X19, the second argument is copied to X20, and the first argument is copied to X22, X23 is set to X21 plus 5, \_\_\_strlcat\_chk is called with SP + 0x370 + var\_358, the third argument, 255, and 255. Now, 4 bytes at SP + 0x370 + var\_353 is compared against 0x2F737470 and if they are not equal X0 is set to X23 else X0 is set to X21 plus 9, W1 is set to 0x2F and \_strchr is called and if the result was 0 PC is set to loc\_100000774, else \_snprintf is called with 0x100008000, 0x400, the address of the string "Too many '/' in \\"%s\\"", and SP + 0x370 + var\_358 and then we return: ![[ida_talkd_26.jpg]] Now, at loc\_100000774: ![[ida_talkd_27.png]] 8 zero bytes are stored at SP + 0x370 + var\_370, X23 and X0 are both set to SP + 0x370 + var\_358, W1 is set to 5 and \_open is called, if bit 0x1F of the result of \_open is not zero then we go to loc\_1000007B8, X21 is set the the result of open and W20 is compared against 1 and if the negative flag does not equal the overflow flag then PC is set to loc\_1000007DC where X24 is set to 0 and PC then is set to loc\_1000007E0, if the comparison with W20 and 1 made the zero flag equal the overflow flag then X24 is set to 0, W8 is set equal to W20, and X9 is set to X22 plus 8 and PC reaches loc\_1000007A4. At loc\_1000007A4, X10 is set to X9 and X9 is then increased by 16, X24 is increased by X10 and X8 is subtracted by 1 and the flags are set because SUBS was used, so if that subtraction made the zero flag 0 we jump to loc\_1000007A4, else PC is set to loc\_1000007E0. Now before we keep going, lets look at loc\_1000007B: ![[ida_talkd_26.jpg]] At loc\_1000007B, \_\_\_error is called and then \_strerror is called with 4 bytes at the result of \_\_\_error, X19 is set to 0x100008000 and snprintf is called with X19, 0x400, the address of the string "%s: %s", X23, and the result of \_strerror and we return. Now back to loc\_1000007E0! ![[ida_talkd_28.png]] At loc\_1000007E0, \_writev is called with X21 that is the result of \_open, X22 and remember that it holds the first argument, and X20 and that is the second argument, if the result of \_writev is greater than X24 we close the file and return 0, else the result of \_writev is copied to X23, W26 is set to 0, and X25 is set to SP + 0x370 + var\_258 and PC is set to loc\_100000804, if bit 0x3F of X23 is zero then PC is set to loc\_100000884 and there, if X22 is equal to X25 then X27 is set to X22 and PC is set to loc\_1000008AC, if X22 was not equal to X25 then the pseudo instruction SBFIZ is used and SBFIZ is just an alias of SBFM, SBFIZ is just: SBFM Rd1, Rs1, #(-imm1 MOD regwidth), imm2 - 1. So, SBFIZ is used with X2, X20, 4, and 0x20. X27 and X0 are both set to SP + 0x370 + var\_258, X1 is set to X22 and X2 is set to 0x200 and \_\_\_memmove_chk is called and PC is set to loc\_1000008AC. So there, X8 is set to 8 bytes at X27 plus 8 and X23 is compared against X8 and if the carry flag was not set because of the comparison then X22 is set to X27 and X9 is set to X23 and PC is set to loc\_1000008E4, on the other arm (arm instead of hand lol) if the carry flag was set because of the comparison then X9 is set to X23, and PC is set to loc\_1000008C8. At loc\_1000008C8, X9 is subtracted by X8, X22 is set to X27 plus 0x10, W20 is subtracted by 1, X8 is set to 8 bytes at X27 plus 0x18 and X9 is compared against X8 and X27 is set to X22 and if the clear flag is set then PC is set to loc\_1000008C8, else PC is set to loc\_1000008E4. Now, back at loc\_10000080, if bit 0x3F was not zero then the following happens. First of all, \_\_\_error is called and if the 4 bytes at the result being treated as an address is not 0x23 then we go to loc\_10000092C, if we do not go to loc\_10000092C then if W26 is not 0 then we close the file and exit the program with an error code of 1, if W26 is 0 then \_fork is called and if bit 0x1F of the result is not zero then we go to loc\_10000095C where snprintf is called with with 0x100008000, 0x400, the address of the string "fork: %s", and the result of \_strerror after error was called and \_strerror was called on the 4 bytes at the result of \_\_\_error being treated as an address, also X19 is set to X0, and X0 holds the buffer lol, snprintf is called and the file is closed and we return. If the result from \_fork's bit 0x1F is 0 then if the result is 0 then we close the file and return 0. If the result from \_fork's bit 0x1F is 0 then if the result is not 0 then \_signal is called with 0xE and 0, it is called again with 0xF and 0 and then \_sigsetmask is called with 0, \_alarm is called with X19, 8 zero bytes are stored at SP, \_fcntl is called with X21 and 4, \_writev is called with X21, X22, and X20, the result of the \_writev is stored in X23, W26 is set to 1, and if the result of \_writev is less than X24 then PC is set to loc\_100000804, else we close the file and exit with a exit code of 0 if X26 is not zero else we return 0. Now, at loc\_10000092C \_\_\_error is called and 4 bytes at the result being treated as an address are stored at W8 and if W8 is equal to 0x13 then we close the file and check if X26 is zero and if it is we return 0 else we exit with an exit code of 0, else \_\_\_error is called and 4 bytes at the result being treated as an address are stored at W8 and if W8 is equal to 5 then we close the file and check if X26 is zero and if it is we return 0 else we exit with an exit code of 0 else if W8 does not equal 5 then the file is closed and if X26 is not zero then we exit with an exit code of 1, else \_\_\_error is called and 4 bytes at the result being treated as an address are stored at W0 and \_strerror is called, X8 is set to SP + 0x370+ var\_358, X19 is set to 0x100008000. Now, \_snprintf is called with X19, 0x400 the address of "%s: %s", X8, and the result of \_strerror and then the function returns. Now at loc\_1000008E4: ![[ida_talkd_26.jpg]] At loc\_1000008E4 if X9 is 0 then PC is set to loc\_1000008F8, else X10 is set 8 bytes at X22 and is then increased by X9 and X8 is decreased by X9, X10 is stored at X22 being treated as an address and X8 is stored at X22 plus 8 being treated as an address, then PC is set to loc\_1000008F8, at loc\_1000008F8 X24 is decreased by X23, \_writev is called with X21, X22, and X20 and X23 is set to the result and if the result is less than X24 then we jump to loc\_100000804 else we close the file and if X26 is zero then we return 0 else we exit with an exit code of 0.... EVERY SINGLE USER DEFINED FUNCTION HAS BEEN DOCUMENTED I FEEL... wait is ntalkd open source? If so I heavily documented it for nothing, but even if it is open source I do NOT care, I got to see how it REALLY worked... Now, I guess its time for part 2... ## Part 2: Bug hunting So then, remember our friend sub\_1000009E4? DO YOU?! Ok I think you do! So sub\_1000009E4 is called with the data we send to it and 0x60 bytes after it. So basically the first byte of our input must be 1, 2 bytes at bit position 8 and 2 bytes at bit position 0x18 must both be equal to 0x200. (BIG TODO)