GithubHelp home page GithubHelp logo

patchkit's Introduction

patchkit

Patches an ELF binary using one or more simple Python scripts.

Usage:

patch <binary> <patchdir|file> [patchdir|file...]

patchdir

Contains one or more Python patch files, which will be executed in alphabetical order against a binary.

Patch Examples

Nopping an address, injecting an assembly function, and hooking the entry point:

def simple_patch(pt):
    # nop out a jump at the entry point
    pt.patch(pt.entry, hex='90' * 5)

    # inject assembly into the binary and return the address
    addr = pt.inject(asm='mov eax, 1; ret')

    # hook the entry point to make it call addr (ret will run the original entry point)
    pt.hook(pt.entry, addr)

Replacing a C function:

def replace_free(pt):
    # pretend free() is at this address:
    old_free = 0x804fc4

    # inject a function to replace free()
    new_free = pt.inject(c=r'''
    void free_stub(void *addr) {
        printf("stubbed free(%p)\n", addr);
    }
    ''')

    # patch the beginning of free() with a jump to our new function
    pt.patch(old_free, jmp=new_free)

API

addr = search(data)
hook(addr, new_addr)
patch(addr, *compile arg*)
addr = inject(*compile arg*)

*compile arg* is any of the following:
  raw='data'
  hex='0bfe'
  asm='nop'
  jmp=0xaddr
  c='void func() { int a; a = 1; }' (only supported on inject, not patch)

IDA scripts

Some scripts live in the ida/ path. Run them like this:

/Applications/IDA\ Pro\ 6.8/idaq.app/Contents/MacOS/idaq64 -A -Sida/allfuncs.py a.out

When invoked like this, allfuncs.py will generate a.out.funcs which is used by hardening scripts.

Tools

These are somewhat CGC and x86-specific right now, but will be ported for general use in the future.

  • explore: uses a Python CFG and recursive backtracking emulator to find basic blocks in an executable
  • bindiff: uses the block boundaries from an explore run, as well as additional analysis to find and output basic block diffs between two binaries

Dependencies

patchkit's People

Contributors

aquynh avatar ba0f3 avatar lunixbochs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

patchkit's Issues

why。 NameError: name 'KS_ARCH_X86' is not defined?

File "./patch", line 6, in
from core import Patcher
File "/home/angr/patchkit-master/core/init.py", line 1, in
from patcher import Patcher
File "/home/angr/patchkit-master/core/patcher.py", line 7, in
from binary import Binary
File "/home/angr/patchkit-master/core/binary.py", line 8, in
from context import Context
File "/home/angr/patchkit-master/core/context.py", line 4, in
import arch
File "/home/angr/patchkit-master/core/arch.py", line 39, in
class x86(Arch):
File "/home/angr/patchkit-master/core/arch.py", line 41, in x86
_ks = KS_ARCH_X86, KS_MODE_32
NameError: name 'KS_ARCH_X86' is not defined

what should I do?

Error when patching a arm binary

The error message:

Traceback (most recent call last):
  File "mine-patch.py", line 35, in <module>
    patch = Patcher(binary)
  File "/home/ver0n1ca/Desktop/testpatch/mine/core/patcher.py", line 11, in __init__
    self.bin = Binary(binary)
  File "/home/ver0n1ca/Desktop/testpatch/mine/core/binary.py", line 29, in __init__
    if ph.isload:
  File "/home/ver0n1ca/Desktop/testpatch/mine/util/elffile.py", line 1434, in isload
    return PT[self.type].name == 'PT_LOAD'
  File "/home/ver0n1ca/Desktop/testpatch/mine/util/elffile.py", line 66, in __getitem__
    return self.bycode[key]
KeyError: 1879048193

The arm binary I use is provided. Please give me a clue, thanks!
simple-arm.zip

powerpc hook support

How to hook a powerpc executable file using patch? I notice that Capstone support PPC. Do patchkit support powerpc?

Replacing a C function Error, Can't injecode c code to elf file

ubuntu:~/patchkit-master$ ./patch fun patch.py

[*] patch.py
 [+] replace_free()
  [LINK] printf
  [LINK] puts
  [LINK] strlen
Exception thrown by patch: /home/haclh/patchkit-master/patch.py replace_free
Traceback (most recent call last):
  File "/home/haclh/patchkit-master/core/patcher.py", line 69, in patch
    func(patchset)
  File "/home/haclh/patchkit-master/patch.py", line 10, in replace_free
    ''')
  File "/home/haclh/patchkit-master/core/context.py", line 301, in inject
    asm = compiler.compile(c, self.binary.linker)
  File "/home/haclh/patchkit-master/core/compiler.py", line 110, in compile
    asm = linker.post(asm, syms=syms)
  File "/home/haclh/patchkit-master/core/linker.py", line 177, in post
    asm = re.sub(find_ref, '0x%x' % self.resolve(ref), asm)
  File "/home/haclh/patchkit-master/core/linker.py", line 106, in resolve
    self.inject(sym)
  File "/home/haclh/patchkit-master/core/linker.py", line 101, in inject
    self.addrs.update(self.syms[sym][1].inject(self, sym))
  File "/home/haclh/patchkit-master/core/linker.py", line 39, in inject
    asm = compiler.compile(self.source, linker, syms=self.syms.keys())
  File "/home/haclh/patchkit-master/core/compiler.py", line 110, in compile
    asm = linker.post(asm, syms=syms)
  File "/home/haclh/patchkit-master/core/linker.py", line 177, in post
    asm = re.sub(find_ref, '0x%x' % self.resolve(ref), asm)
  File "/home/haclh/patchkit-master/core/linker.py", line 106, in resolve
    self.inject(sym)
  File "/home/haclh/patchkit-master/core/linker.py", line 101, in inject
    self.addrs.update(self.syms[sym][1].inject(self, sym))
  File "/home/haclh/patchkit-master/core/linker.py", line 39, in inject
    asm = compiler.compile(self.source, linker, syms=self.syms.keys())
  File "/home/haclh/patchkit-master/core/compiler.py", line 110, in compile
    asm = linker.post(asm, syms=syms)
  File "/home/haclh/patchkit-master/core/linker.py", line 177, in post
    asm = re.sub(find_ref, '0x%x' % self.resolve(ref), asm)
  File "/home/haclh/patchkit-master/core/linker.py", line 106, in resolve
    self.inject(sym)
  File "/home/haclh/patchkit-master/core/linker.py", line 101, in inject
    self.addrs.update(self.syms[sym][1].inject(self, sym))
  File "/home/haclh/patchkit-master/core/linker.py", line 45, in inject
    raw = pt.asm(asm, addr=addr, att_syntax=True)
  File "/home/haclh/patchkit-master/core/context.py", line 135, in asm
    return self.arch.asm(asm, addr=addr, att_syntax=att_syntax)
  File "/home/haclh/patchkit-master/core/arch.py", line 22, in asm
    return ''.join(map(chr, tmp))
TypeError: argument 2 to map() must support iteration

the patch.py source

def replace_free(pt):
    # pretend free() is at this address:
    old_free = 0x804fc4

    # inject a function to replace free()
    new_free = pt.inject(c=r'''
    void free_stub(void *addr) {
        printf("stubbed free(%p)\n", addr);
    }
    ''')

binary bloat

examine the binary size under different scenarios, including the defcon ctf binaries (450kb > 750kb)

Currently real-world ELFs (like ubuntu's ls) will be re-emitted at around 2MB heavier, due to alignment hacks. I need to determine why the alignment padding is necessary and if it can be reduced.

rewrite ELF loader

I don't want the coding dependency, and elffile is a bit fragile and could have cleaner enums

sample to doctor a file

For example elf32mod: IDA Loader plugin to properly load ELF32 files, which don't have Section Header/or it's corrupted which is never used during ELF file loading into memory

By internally loading a file and re-emitting it, we kinda validate it. Can also add a flag to generate guessed sections.

replacing a C function doesn't work

Hi, I used this tool to hook a jle instruction in a binary. But the instrumented function doesn't work. The c code of the binary is as follows:

#include<stdio.h>
int main(){
	int n;
	scanf("%d",&n);
	if(n>10){
		printf("Yes");
	}
	else
		printf("No");
}

my patch file:

def replace_jmp(pt):
    # pretend jmp is at this address:
    old_jmp =  0x400629

    # inject a function to replace jmp()
    addr = pt.inject(c=r'''
    void jmp_stub() {
        printf("stubbed jmp\n");
        return;
    }
    ''')

    # hook the entry point to make it call addr (ret will run the original entry point)
    pt.hook(old_jmp, addr)

the addr of the binary:

 0x400623 	 mov	 eax, dword ptr [rbp - 0xc]
 0x400626 	 cmp	 eax, 0xa //cmp input with 10
 0x400629 	 jle	 0x40063c 
 0x40062b 	 mov	 edi, 0x4006f7
 0x400630 	 mov	 eax, 0

The output of patch:

[*] test_patch.py
 [+] replace_jmp()
  [LINK] printf
  [LINK] puts
  [LINK] strlen
  [INJECT] @0x1e01000-0x1e0100c
  [LINK] transmit
  [INJECT] @0x1e0100c-0x1e01022
  [INJECT] @0x1e01022-0x1e0103b
  [LINK] strupr
  [LINK] toupper
  [LINK] islower
  [INJECT] @0x1e0103b-0x1e01047
  [INJECT] @0x1e01047-0x1e0105b
  [INJECT] @0x1e0105b-0x1e01076
  [LINK] putc
  [INJECT] @0x1e01076-0x1e01099
  [LINK] itoa (includes [atoi, itoa])
  [INJECT] @0x1e01099-0x1e01119
  [INJECT] @0x1e01119-0x1e0139f
  [INJECT] @0x801000-0x80101b
  [INJECT] @0x801033-0x80105a
  [INJECT] @0x80105a-0x80107c
  [PATCH] @0x80101b-0x801027 | "hook stage 1"
  [PATCH] @0x801027-0x801033 | "hook stage 2"
  [PATCH] @0x400629-0x400635 | "hook entry point"

No error in it. But when i run the patch binary, the "stubbed jmp" wasn't print out.
I use gdb to debug the patched binary, it jumped into the instrumented function but exit normally before printf("stubbed jmp").
Could anyone give me a hint please?

sample to static link a compiled dynamic library into a compiled binary

  1. combine segments (not by extending binary's existing segments, but by adding new ones)
  2. change relocation offsets for the library and add its relocations to the binary's relo table
  3. add all library symbols to binary relo table
  4. don't think I need to preemptively do relocations for any symbols from the library, as long as ld.so considers the binary's own symtab when loading
  5. add all library symbols to symtab
  6. merge library dependencies
  7. re-emit combined binary!
  8. repeat as necessary

Can the patched file size be reduced?

Hi. I was trying to patch some binaries and found that the binary became much bigger after patched. For example, a 15KB file become 2.1MB (139 times bigger). If we only need to inject few codes, can the size be reduced? Cause for small binaries, this seems a bit unaffordable ...
Thanks.

How to hook the end of a function

Currently, we can hook the begin of a function。How to hook the end of a function? So that we can check the return value of the function, or execute some code after the function end.

Elffile clobbers .bss if there is only one loadable segment

Using the following C program to tell if global (.bss) data is corrupted:

#include <stdio.h>
long global_nums[255];
int main(){
        int i;
        for (i=0;i<255;++i){
                if (global_nums[i] != 0){
                        puts("Corrupted global (.bss)");
                        return -1; 
                }
        }
        puts("No errors.");
        return 0;
}

The binary is compiled with GCC and linked with a custom linker script that only produces a single LOADable section that results in the following segment structure:

root@c0f431c06c51:/patchkit# readelf -l test

Elf file type is EXEC (Executable file)
Entry point 0x80482d0
There are 7 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
  INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x006c4 0x00afc RWE 0x1000
  DYNAMIC        0x0005bc 0x080485bc 0x080485bc 0x000e8 0x000e8 RW  0x4
  NOTE           0x000128 0x08048128 0x08048128 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0004b8 0x080484b8 0x080484b8 0x0002c 0x0002c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.got .rel.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
   03     .dynamic
   04     .note.ABI-tag .note.gnu.build-id
   05     .eh_frame_hdr
   06

Test modification via elffile (some ripped from core/binary.py):

from util import elffile
from util.elffile import PT
import sys

align = 0x1000
start = 0xFFFFFFFFFFFFFFFF
end = 0
e = elffile.open(sys.argv[1])
for ph in reversed(e.progs):
    if ph.isload:
        start = min(start, ph.vaddr)
        end = max(ph.vaddr + ph.vsize, end)

addr = end
ph = e.programHeaderClass()
ph.data = bytearray("hello world")
ph.type = PT['PT_LOAD'].code
ph.vaddr = (addr + align - 1) & ~(align - 1)
ph.paddr = ph.vaddr
ph.flags = 7
ph.align = align
ph.memsz = 0
ph.filesz = 0
e.progs.append(ph)
e.save(sys.argv[1]+".mod")

Patching in a new segment with patchkit's elffile implementation (via the script above) results in the error being printed. elffile is writing the program header table over where the .bss would be resulting in corruption of the data when it is loaded into memory (.bss has phdr data in it instead of 0s).
Running test:

root@c0f431c06c51:/patchkit# ./test
No errors.

Running test.mod:

root@c0f431c06c51:/patchkit# ./test.mod
Corrupted global (.bss)

Problem: when the memsz > filesz of a LOAD segment it is unsafe to just append the phdr table to it since the data in the space between (vaddr+filesz) and (vaddr+memsz) is expected to be 0.
Solution: Allocate the slack space manually if memsz > filesz of the first LOADable segment and put the phdr table after it (util/elffile.py line 871-878)

        for p in self.progs:
            if p.offset == 0 and PT[p.type] == PT['PT_LOAD']:
                # HACK: put PHDR at the end of the first segment
                if p.memsz > p.filesz:  # added this if statement
                     # account for .bss
                     p.data.extend(bytes('\x00' * (p.memsz - p.filesz)))
                     p.filesz = p.memsz
                phoff = p.offset + len(p.data)

Patching and running the test binary now results in a clean run.
I think this solution will be fine, but I don't want to mess up the main branch so I am merely suggesting the change here.
My only concern is now a SHT_NOBITS section will have bits in the file... but sections aren't actually needed so I don't think it matters that much.

The hook api

Hello,I really like this project and It help me a lot .
I found the hook api is not work well with pie .I see the patch and found that when hook paste origin code back ,It use a hardcore address.

handle half-instruction patches better

if you patch only part of an instruction in assembly mode, the patchkit diff is broken
at least print hex, or expand the diff to the larger instruction size and put hex in the tail of the + section

optionally nop the remainder of the overlap

elf segment offset error

When calculating the offsets for LEF segments in ElfFile._offsets(), there's something like:

if p.offset is 0:
    p.filesz += phsize
    x = offset + p.filesz

However, some binaries may contain multiple segments with zero file offset. In the following example, the offset of GNU_STACK segment is also zero, which results in a wrong offset calculation of segments after it (if we want to append segments to this ELF).

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  PHDR           0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R   0x8
  INTERP         0x017d80 0x0000000000017d80 0x0000000000017d80 0x00001e 0x00001e R   0x10
      [Requesting program interpreter: /usr/lib/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x006fa0 0x006fa0 R   0x1000
  LOAD           0x007000 0x0000000000007000 0x0000000000007000 0x00f9a5 0x00f9a5 R E 0x1000
  LOAD           0x017000 0x0000000000017000 0x0000000000017000 0x0044c0 0x0044c0 R   0x1000
  LOAD           0x01b8e8 0x000000000001c8e8 0x000000000001c8e8 0x000768 0x0048c0 RW  0x1000
  DYNAMIC        0x01baa0 0x000000000001caa0 0x000000000001caa0 0x000230 0x000230 RW  0x8
  NOTE           0x000318 0x0000000000000318 0x0000000000000318 0x000020 0x000020 R   0x8
  NOTE           0x000338 0x0000000000000338 0x0000000000000338 0x000044 0x000044 R   0x4
  GNU_PROPERTY   0x000318 0x0000000000000318 0x0000000000000318 0x000020 0x000020 R   0x8
  GNU_EH_FRAME   0x017da0 0x0000000000017da0 0x0000000000017da0 0x0009bc 0x0009bc R   0x4
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
  GNU_RELRO      0x01b8e8 0x000000000001c8e8 0x000000000001c8e8 0x000718 0x000718 R   0x1

The patched binary differs when switching to another computer

Hi, I used exactly same version of patchkit, same patchfiles and same binary on two Ubuntu 16.04 computers. The output of patching is the same and without any errors but the patched binary is different. One works well but the other has segmentation fault. I found that the jump address of the patch position is different:

//No.1
.text:0000000000400629                 jmp     near ptr 80104Bh
//No.2
.text:0000000000400629                 jmp     near ptr 8392779h

And I found that hex(8392779)=0x80104B

The output of ./patch:

[*] 0x400629.py
 [+] patch()
  [INJECT] @0x801483-0x8014a0
  [INJECT] @0x8014a0-0x8014c7
  [INJECT] @0x8014df-0x801506
  [INJECT] @0x801506-0x801528
  [PATCH] @0x8014c7-0x8014d3 | "hook stage 1"
  [PATCH] @0x8014d3-0x8014df | "hook stage 2"
  [PATCH] @0x400629-0x400635 | "hook entry point"

I don't know what's happening, please give me I clue :(

ELF patched by newly installed patchkit always fails segmentation fault

I installed patchkit on newly installed ubuntu(18.04 and 20.04)
after run ./deps.sh,it shows

All done!

Testing Python import: Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named keystone

so I manually cd to build/keystone/bindings/python and run python setup.py install and it seems work.
but actually, some address is obviously incorrect.

ubuntu@VM-16-12-ubuntu:~/patchkit$ ls
bindiff  build  core  deps.sh  explore  hpwnwaf2.py  ida  LICENSE  patch  pwn_test  README.md  run  samples  util
ubuntu@VM-16-12-ubuntu:~/patchkit$ vi hpwnwaf2.py 
ubuntu@VM-16-12-ubuntu:~/patchkit$ ./patch -v ./pwn_test hpwnwaf2.py
[*] hpwnwaf2.py
 [+] replace_waf()
  [INJECT] @0x801000-0x8010c5
  ......
  [HOOK] @0x400583 -> 0x801000
  [!] Segment made writable: 0x400000-0x400784
  [INJECT] @0x8010e1-0x801108
  0x8010e1: e81affffff     call 0x801000
  0x8010e6: 57             push rdi
  0x8010e7: 56             push rsi
  0x8010e8: 51             push rcx
  0x8010e9: 488d3ddd8de6fb lea rdi, [rip - 0x4197223]         <========= here rip - 0x4197223 is incorrect
  0x8010f0: 488d35d6ffffff lea rsi, [rip - 0x2a]
  0x8010f7: 48c7c114000000 mov rcx, 0x14
  0x8010fe: f3a4           rep movsb byte ptr [rdi], byte ptr [rsi]
  0x801100: 59             pop rcx
  0x801101: 5e             pop rsi
  0x801102: 5f             pop rdi
  0x801103: e97bf4bfff     jmp 0x400583
  [INJECT] @0x801108-0x80112a
  0x801108: 57             push rdi
  0x801109: 56             push rsi
  0x80110a: 51             push rcx
  0x80110b: 488d3da38de6fb lea rdi, [rip - 0x419725d]         <========= and here is also incorrect
  0x801112: 488d3588ffffff lea rsi, [rip - 0x78]
  0x801119: 48c7c114000000 mov rcx, 0x14
  0x801120: f3a4           rep movsb byte ptr [rdi], byte ptr [rsi]
  0x801122: 59             pop rcx
  0x801123: 5e             pop rsi
  0x801124: 5f             pop rdi
  0x801125: e962f4bfff     jmp 0x40058c
  [PATCH] @0x8010c5-0x8010d3 | "hook stage 1"
  - 0000000000000000000000000000
  + 0x8010c5: e9590b4000 jmp 0xc01c23
  + 0x8010ca: 90909090   nop (x4)
  + 0x8010ce: e89ffeffff call 0x800f72
  [PATCH] @0x8010d3-0x8010e1 | "hook stage 2"
  - 0000000000000000000000000000
  + 0x8010d3: 55         push rbp
  + 0x8010d4: 4889e5     mov rbp, rsp
  + 0x8010d7: bf27064000 mov edi, 0x400627
  + 0x8010dc: e9770b4000 jmp 0xc01c58
  [PATCH] @0x400583-0x400591 | "hook entry point"
  - 0x400583: 55         push rbp
  - 0x400584: 4889e5     mov rbp, rsp
  - 0x400587: bf27064000 mov edi, 0x400627
  - 0x40058c: e89ffeffff call 0x400430
  + 0x400583: e9590b4000 jmp 0x8010e1
  + 0x400588: 90909090   nop (x4)
  + 0x40058c: e89ffeffff call 0x400430

[+] Saving binary to: /home/ubuntu/patchkit/pwn_test.patched
ubuntu@VM-16-12-ubuntu:~/patchkit$ ./pwn_test.patched 
Segmentation fault (core dumped)               <=============== and the ELF fails segmentation fault

A problem when patching arm binary with dyn branch

When patching arm binary with samples/arm/hello32.py, sometimes the patched binary ended in a infinite loop because the LR register is changed in trampoline so later in the program when it jumps to the address in LR without assigning it to a new address, it jumps back to the injected instructions.

I edited the call function in class arm in arch.py. It worked in my case.

def call(self, dst):
        return '''
        push {lr}
        bl %s
        pop {lr}
        ''' % self.fmtaddr(dst)

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.