Saturday, May 9, 2009

Banning SCHelper from syslog

(Here is the necessary change required, for 2.2.1, if you don't bother to read.)

(Make sure you back up every file you want to change first. We won't take responsibility for your broken devices :p)

syslog is a very useful tool for watching variables and monitoring
system status. But SCHelper make it particular troublesome to follow the messages.

SCHelper stands to System Configuration Helper, and it manages the NSUserDefaults writes. In regular interval SCHelper will synchronize the NSUserDefaults on RAM to the harddisk, and report it in syslog. The problem is, the report takes 7 to 13 lines, and is completely useless to know:

May 9 15:55:16 --- SCHelper[1491]: per-session socket : invalidate fd 9
May 9 15:55:21 --- SCHelper[1491]: processing command "AUTH" w/data
May 9 15:55:21 --- SCHelper[1491]: sending status 0
May 9 15:55:21 --- SCHelper[1491]: processing command "PREFS open" w/data
May 9 15:55:21 --- SCHelper[1491]: sending status 0
May 9 15:55:21 --- SCHelper[1491]: processing command "PREFS lock/wait"
May 9 15:55:21 --- SCHelper[1491]: sending status 0
May 9 15:55:21 --- SCHelper[1491]: processing command "PREFS commit" w/data
May 9 15:55:21 --- SCHelper[1491]: sending status 0 w/reply
May 9 15:55:21 --- SCHelper[1491]: processing command "PREFS unlock"
May 9 15:55:21 --- SCHelper[1491]: sending status 0
May 9 15:55:21 --- SCHelper[1491]: processing command "PREFS close"
May 9 15:55:21 --- SCHelper[1491]: processing command "EXIT"

Usually one watches the syslog using tail -f /var/log/syslog. When SCHelper is in action, suddenly a dozen lines come up and you may miss what you need to know. (Of course you can filter the result using grep, but it's just pretending the problem does not exist.)

To solve the root cause we need to disable SCHelper from yelling. A starting point is to disassemble SCHelper. The file is located at /System/Library/Frameworks/SystemConfiguration.framework/SCHelper. We can search for the keyword processing command in the disassembly, and these line should come up:

+00104 000023c4 0100A0E3 mov r0,#0x1
+00108 000023c8 0710A0E3 mov r1,#0x7
+0010c 000023cc 98219FE5 ldr r2,[pc,#0x198] ; -> 0x256c CFSTR("processing command \"%s\"%s")
+00110 000023d0 960100EB bl SCLog (stub)

This is equivalent to the call SCLog(1, 7, CFSTR("processing command \"%s\"%s"), <other arguments>). What, yet another Log function? Hell yes.

This SCLog is an external symbol. We can play a dirty trick to transform all SCLog into ilogb :p. We need to edit the link table for this. The first obvious step is to search for the string _SCLog and replace it to _ilogb (or your favorite pure function). But this will make dyld complain not finding _ilogb in SystemConfiguration (_ilogb is in libSystem.B.dylib). We must change the dylib referenced by this particular symbol as well.

How dyld searches identify a stub symbol? From the Mach-O ABI, we can see it involves a lot of indirection:
  1. Obtain the indirect symbol table from the dynamic symbol table (LC_DYSYMTAB load command)
  2. Every entry of the indirect symbol table points to an index of the symbol table (LC_SYMTAB load command)
  3. An entry of a symbol table is better known as nlist. In our case, the nlist contains reference to the dylib (an LC_LOAD_DYLIB load command) and an offset in the string table.
  4. Finally from the string table we get the actual C string.

What we get is the offset of the C string, _SCLog. We can go up 1 level from there (to the nlist), and modify the dylib it references, then it should work.

For this task, otool and nm is of great help.

Using otool -L SCHelper we can see that the string table (LC_SYMTAB's stroff) is at file offset 15024 (0x3ab0). Additional, the file offset of the string _SCLog is at 0x3cd0. Therefore, the string table offset is 0x3cd0 - 0x3ab0 = 0x220.

Then, in the same load command we can find the symbol table's file offset (symoff) is 12832 (0x3220).

Now, to quickly search for a symbol, we use nm -p SCHelper | nl -v 0. (The -p flags tells nm don't try to sort the symbol table, and the nl part is a Unix utility for line numbering.) We can find _SCLog at 67, meaning we should be able to find the nlist of _SCLog at (0x3220 + 67 * sizeof(struct nlist) = 0x3544).

So let's proceed to 0x3544. The structure of nlist, for our concern is
struct nlist {
int symbol_index;
char lets_not_bother_it_its_always_0x0D;
char lets_not_bother_it_its_always_zero;
char lets_not_bother_it_its_always_0x01;
char the_dylib;
void* the_vm_address_that_we_dont_care;
We can verify the first 4 bytes are indeed 0x220, so this is the correct address. For _SCLog, the_dylib = 2, and the dylib referenced are, in order:
  1. /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
  2. /System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration
  3. /System/Library/Frameworks/Security.framework/Security
  4. /usr/lib/libgcc_s.1.dylib
  5. /usr/lib/libSystem.B.dylib

so we should change the 2 into 5.

I believe there is some link editor that doesn't require us to jump so many hoops. If you know, please leave a comment :).

In summary, the changes are (if you are using 2.2.1):
  1. Open SCHelper with a hex editor.
  2. Head to 0x354B, change the 02 to 05
  3. Search for the string "SCLog", replace it with "ilogb".
  4. Save, and of course, ldid it.

Now respring, and ps -A. Is SCHelper running? Yes, great. Does it emit useless syslog entries anymore? No, it's busy computing logarithms instead :p. Now launch Settings, fiddle with anything. Does it save? Yes, fantastic.

So our task succeeded. Note that if you are using any versions other than 2.2.1, the file offsets may be different.

No comments:

Post a Comment